CustomControl with a ComboBox - c#

I'm having troubles creating a custom control with a ComboBox.
This is my simple code:
public class MyComboBox : Control
{
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for ItemsSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(MyComboBox), new UIPropertyMetadata(null));
public string DisplayMemberPath
{
get { return (string)GetValue(DisplayMemberPathProperty); }
set { SetValue(DisplayMemberPathProperty, value); }
}
// Using a DependencyProperty as the backing store for DisplayMemberPath. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DisplayMemberPathProperty =
DependencyProperty.Register("DisplayMemberPath", typeof(string), typeof(MyComboBox), new UIPropertyMetadata(""));
public string SelectedValuePath
{
get { return (string)GetValue(SelectedValuePathProperty); }
set { SetValue(SelectedValuePathProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedValuePath. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedValuePathProperty =
DependencyProperty.Register("SelectedValuePath", typeof(string), typeof(MyComboBox), new UIPropertyMetadata(""));
public object SelectedValue
{
get { return (object)GetValue(SelectedValueProperty); }
set { SetValue(SelectedValueProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedValueProperty =
DependencyProperty.Register("SelectedValue", typeof(object), typeof(MyComboBox), new UIPropertyMetadata(null));
public int SelectedIndex
{
get { return (int)GetValue(SelectedIndexProperty); }
set { SetValue(SelectedIndexProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedIndex. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedIndexProperty =
DependencyProperty.Register("SelectedIndex", typeof(int), typeof(MyComboBox), new UIPropertyMetadata(0));
static MyComboBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyComboBox), new FrameworkPropertyMetadata(typeof(MyComboBox)));
}
}
and this is its Generic.xaml:
<Style TargetType="{x:Type local:MyComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyComboBox}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="MyComboBox" />
<ComboBox Grid.Column="1"
ItemsSource="{Binding Path=ItemsSource, RelativeSource={RelativeSource Mode=TemplatedParent}}"
SelectedIndex="{Binding Path=SelectedIndex, RelativeSource={RelativeSource Mode=TemplatedParent}}"
DisplayMemberPath="{Binding Path=DisplayMemberPath, RelativeSource={RelativeSource Mode=TemplatedParent}}"
SelectedValuePath="{Binding Path=SelectedValuePath, RelativeSource={RelativeSource Mode=TemplatedParent}}"
SelectedValue="{Binding Path=SelectedValue, RelativeSource={RelativeSource Mode=TemplatedParent}}">
</ComboBox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
To test it, I made a simple WPF application with this MainWindow.xaml:
<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Example"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ComboBox Grid.Row="0" Grid.Column="0" Margin="4" VerticalAlignment="Center"
ItemsSource="{Binding Path=Numbers}"
DisplayMemberPath="Key"
SelectedValuePath="Value"
SelectedValue="{Binding Path=Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Row="0" Grid.Column="1" Margin="4" HorizontalAlignment="Center" VerticalAlignment="Center"
Text="{Binding Path=Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<local:MyComboBox Grid.Row="1" Grid.Column="0" Margin="4" VerticalAlignment="Center"
ItemsSource="{Binding Path=MyNumbers}"
DisplayMemberPath="Key"
SelectedValuePath="Value"
SelectedValue="{Binding Path=MyNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Row="1" Grid.Column="1" Margin="4" HorizontalAlignment="Center" VerticalAlignment="Center"
Text="{Binding Path=MyNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Window>
and this ViewModel:
public class ViewModel : INotifyPropertyChanged
{
private int _number;
public int Number
{
get { return _number; }
set
{
_number = value;
OnPropertyChanged("Number");
}
}
public Dictionary<string, int> Numbers { get; set; }
private int _myNumber;
public int MyNumber
{
get { return _myNumber; }
set
{
_myNumber = value;
OnPropertyChanged("MyNumber");
}
}
public Dictionary<string, int> MyNumbers { get; set; }
public ViewModel()
{
Numbers = new Dictionary<string, int>()
{
{ "One", 1 },
{ "Two", 2 },
{ "Three", 3 }
};
Number = 1;
MyNumbers = new Dictionary<string, int>()
{
{ "Four", 4 },
{ "Five", 5 },
{ "Six", 6 }
};
MyNumber = 4;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name)
{
PropertyChangedEventHandler e = PropertyChanged;
if (e != null)
{
e(this, new PropertyChangedEventArgs(name));
}
}
}
When I start it, my custom control has a red border and the output Window of Visual Studio signal this error:
System.Windows.Data Error: 23 : Cannot convert '[Four, 4]' from type 'KeyValuePair`2' to type 'System.Int32' for 'en-US' culture with default conversions; consider using Converter property of Binding. NotSupportedException:'System.NotSupportedException: Int32Converter cannot convert from System.Collections.Generic.KeyValuePair`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].
at System.ComponentModel.TypeConverter.GetConvertFromException(Object value)
at System.ComponentModel.TypeConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
at System.ComponentModel.BaseNumberConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
at MS.Internal.Data.DefaultValueConverter.ConvertHelper(Object o, Type destinationType, DependencyObject targetElement, CultureInfo culture, Boolean isForward)'
System.Windows.Data Error: 7 : ConvertBack cannot convert value '[Four, 4]' (type 'KeyValuePair`2'). BindingExpression:Path=MyNumber; DataItem='ViewModel' (HashCode=55591935); target element is 'MyComboBox' (Name=''); target property is 'SelectedValue' (type 'Object') NotSupportedException:'System.NotSupportedException: Int32Converter cannot convert from System.Collections.Generic.KeyValuePair`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].
at MS.Internal.Data.DefaultValueConverter.ConvertHelper(Object o, Type destinationType, DependencyObject targetElement, CultureInfo culture, Boolean isForward)
at MS.Internal.Data.ObjectTargetConverter.ConvertBack(Object o, Type type, Object parameter, CultureInfo culture)
at System.Windows.Data.BindingExpression.ConvertBackHelper(IValueConverter converter, Object value, Type sourceType, Object parameter, CultureInfo culture)'
The problem disappear when I remove the property SelectedIndex from the Generic.xaml, but I need it because I want that who will use my control, can have the same basic functionality of a ComboBox.
Anyone knows how to solve it?

I find the solution by myself.
The problem is on the default value I inserted for SelectedIndex: it must be -1 and not 0.

At first it seemed to me your problem is in SelectedValue. In you VM it has type of int, but WPF expects it to be KeyValuePair. The type of the collection's item.
ItemsSource="{Binding Path=MyNumbers}" // KeyValuePair
DisplayMemberPath="Key"
SelectedValuePath="Value"
SelectedValue="{Binding Path=MyNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" // int
Then I realized my mistake that SelectedItem has to be of KeyValuePair type.
Error message looks as WPF does not look through the items' property given by the SelectedValuePath but tries to conver it explicitly to KeyValue pair. That is not documented behavior.

Related

UWP ICommand DependencyProperty cast error

I have a problem with binding an DependencyProperty of type ICommand.
This is my custom user control:
<UserControl x:Class="HexEditor.Controls.NavButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:HexEditor.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="400"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<ToggleButton x:Name="toggleWrap"
Width="140"
Height="48"
Background="Transparent"
BorderThickness="0"
Command="{Binding Command}"
CommandParameter="{Binding CommandParameter}"
IsChecked="{Binding IsChecked}">
<Grid Width="140">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<FontIcon Margin="6,0,0,0"
Glyph="{Binding Icon}" />
<TextBlock Grid.Column="1"
Margin="32,0,0,0"
Text="{Binding Title}" />
</Grid>
</ToggleButton>
</UserControl>
This is code behind of my control:
public sealed partial class NavButton : UserControl
{
public NavButton()
{
this.InitializeComponent();
}
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register("Icon", typeof(string), typeof(FontIcon), new PropertyMetadata(""));
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(TextBlock), new PropertyMetadata(""));
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof(bool), typeof(ToggleButton), new PropertyMetadata(""));
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(ToggleButton), new PropertyMetadata(""));
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object), typeof(ToggleButton), new PropertyMetadata(""));
public string Icon
{
get { return (string)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public bool IsChecked
{
get { return Convert.ToBoolean(GetValue(IsCheckedProperty)); }
set { SetValue(IsCheckedProperty, value); }
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
}
And this is a page where i use my control:
<UserControl x:Class="HexEditor.Common.RootFrame"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="using:HexEditor.Controls"
xmlns:Interactions="using:Microsoft.Xaml.Interactions.Core"
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:HexEditor.Common"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="400"
DataContext="{Binding CommonViewModel,
Source={StaticResource Locator}}"
mc:Ignorable="d">
<Grid Background="White">
<SplitView x:Name="navSplitView"
Width="Auto"
DisplayMode="CompactOverlay"
OpenPaneLength="140">
<SplitView.Pane>
<StackPanel>
<ToggleButton x:Name="hamBtn"
Width="48"
Height="48"
BorderThickness="0">
<FontIcon Glyph="" />
</ToggleButton>
<Controls:NavButton Title="Home"
Command="{Binding NavigationCommand}"
CommandParameter="HomePage"
Icon=""
IsChecked="True" />
<Controls:NavButton Title="Settings"
Command="{Binding NavigationCommand}"
CommandParameter="SettingsPage"
Icon=""
IsChecked="False" />
</StackPanel>
</SplitView.Pane>
<SplitView.Content>
<Frame x:Name="NavFrame" />
</SplitView.Content>
<Interactivity:Interaction.Behaviors>
<Interactions:DataTriggerBehavior Binding="{Binding IsChecked,
ElementName=hamBtn}"
Value="True">
<Interactions:ChangePropertyAction PropertyName="IsPaneOpen"
TargetObject="{Binding ElementName=navSplitView}"
Value="True" />
</Interactions:DataTriggerBehavior>
<Interactions:DataTriggerBehavior Binding="{Binding IsChecked,
ElementName=hamBtn}"
Value="False">
<Interactions:ChangePropertyAction PropertyName="IsPaneOpen"
TargetObject="{Binding ElementName=navSplitView}"
Value="False" />
</Interactions:DataTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</SplitView>
</Grid>
</UserControl>
Here's the ViewModel:
public class CommonViewModel : ViewModelBase
{
public CommonViewModel()
{
this.NavigationCommand = new RelayCommand<NavigationSource>(this.ExecuteNavigationCommand);
}
public ICommand NavigationCommand { get; private set; }
private void ExecuteNavigationCommand(NavigationSource state)
{
NavigationProvider.Instance.NavigateTo(state);
}
}
When compiling, exception appears:
Exception thrown: 'System.InvalidCastException' in HexEditor.exe
'HexEditor.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'C:\Users\Root\documents\visual studio 2015\Projects\HexEditor\HexEditor\bin\x86\Debug\AppX\System.Resources.ResourceManager.dll'. Module was built without symbols.
Exception thrown: 'Windows.UI.Xaml.Markup.XamlParseException' in HexEditor.exe
WinRT information: Failed to assign to property 'HexEditor.Controls.NavButton.Command'. [Line: 29 Position: 41]
An exception of type 'Windows.UI.Xaml.Markup.XamlParseException' occurred in HexEditor.exe but was not handled in user code
WinRT information: Failed to assign to property 'HexEditor.Controls.NavButton.Command'. [Line: 29 Position: 41]
Additional information: The text associated with this error code could not be found.
Failed to assign to property 'HexEditor.Controls.NavButton.Command'. [Line: 29 Position: 41]
What i'm doing wrong?
I think the problem is that you have default values of "" for dependency properties of type bool and ICommand (new PropertyMetadata("") in both cases). You can't cast a string to either one, and that will cause an exception to be thrown. IsChecked should default to false, and Command should default to null.
This may or may not be relevant to the exact issue you're asking about, but it matters too: The third parameter of DependencyProperty.Register() should be the type of the owning class. That's typeof(NavButton) in your case, for all of its dependency properties.

Tab-Focus on custom TextBox

In my application I have a TabControl. On one TabItem there are three TextBoxes where I can switch between them by pressing the Tab-Key.
Now I want to replace this standard-TextBoxes with custom-TextBoxes which should have a Null-Text that will be displayed if the Text is empty.
The XAML of my custom-TextBox is:
<UserControl x:Class="MyApplication.Controls.NullTextTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:converter="clr-namespace:ScM.Converter"
mc:Ignorable="d" d:DesignHeight="24" d:DesignWidth="300"
x:Name="nullTextTextBox" IsHitTestVisible="True" Focusable="True">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" VerticalAlignment="Stretch" x:Name="tbInput"
Text="{Binding ElementName=nullTextTextBox,Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
AcceptsReturn="{Binding ElementName=nullTextTextBox, Path=AcceptsReturn, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
TextWrapping="{Binding ElementName=nullTextTextBox, Path=TextWrapping, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
IsTabStop="True" />
<TextBlock Grid.Column="0" VerticalAlignment="Top" Text="{Binding ElementName=nullTextTextBox,Path=NullText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left"
FontStyle="Italic" Foreground="DarkGray" Margin="4,4,0,0" IsHitTestVisible="False"
Visibility="{Binding ElementName=nullTextTextBox, Path=Text, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Converter={converter:StringIsNullToVisibilityConverter}}"
Focusable="False"/>
<TextBlock Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock.Visibility>
<MultiBinding Converter="{converter:DeleteButtonMultiConverter}">
<Binding ElementName="nullTextTextBox" Path="IsClearButtonVisible" Mode="OneWay" UpdateSourceTrigger="PropertyChanged"/>
<Binding ElementName="nullTextTextBox" Path="Text" Mode="OneWay" UpdateSourceTrigger="PropertyChanged"/>
</MultiBinding>
</TextBlock.Visibility>
<Hyperlink TextDecorations="{x:Null}" Command="{Binding ElementName=nullTextTextBox, Path=ClearTextCommand, Mode=OneWay}"
Focusable="False" >
<TextBlock FontFamily="Wingdings 2" Text="Î" Foreground="Red" FontWeight="Bold" FontSize="14" VerticalAlignment="Center" Margin="1,1,2,1"/>
</Hyperlink>
</TextBlock>
</Grid>
</UserControl>
I would say the code-behind of this xaml isn't relevant, because there are only DependencyProperties registered.
My default-TextBox behaves like I expect it. But if I press the Tab-Key while the focus is within one NullTextBox the focus is switched to the TabHeader and not to the second NullTextBox.
The xaml where the NullTextBoxes are located it looks like:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<controls:NullTextTextBox Grid.Row="0" NullText="Value 1"/>
<controls:NullTextTextBox Grid.Row="1" NullText="Value 2"/>
<controls:NullTextTextBox Grid.Row="2" NullText="Value 3"/>
</Grid>
Why does my second and third NullTextBox not get focused when I press the Tab-Key?
I've found out that if I remove the TextBlock which contains the Hyperlink the Tab-Order works as expected. But I need this TextBlock...
The code-behind of my custom textbox looks like:
public partial class NullTextTextBox : UserControl, INotifyPropertyChanged
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof (string), typeof (NullTextTextBox), new PropertyMetadata(default(string)));
public static readonly DependencyProperty NullTextProperty = DependencyProperty.Register(
"NullText", typeof (string), typeof (NullTextTextBox), new PropertyMetadata(default(string)));
public static readonly DependencyProperty IsClearButtonVisibleProperty = DependencyProperty.Register(
"IsClearButtonVisible", typeof (bool), typeof (NullTextTextBox), new PropertyMetadata(default(bool)));
public static readonly DependencyProperty AcceptsReturnProperty = DependencyProperty.Register(
"AcceptsReturn", typeof (bool), typeof (NullTextTextBox), new PropertyMetadata(default(bool)));
public static readonly DependencyProperty TextWrappingProperty = DependencyProperty.Register(
"TextWrapping", typeof (TextWrapping), typeof (NullTextTextBox), new PropertyMetadata(default(TextWrapping)));
public TextWrapping TextWrapping
{
get { return (TextWrapping) GetValue(TextWrappingProperty); }
set
{
SetValue(TextWrappingProperty, value);
OnPropertyChanged();
}
}
private ICommand clearTextCommand;
public NullTextTextBox()
{
InitializeComponent();
IsClearButtonVisible = false;
Text = string.Empty;
NullText = "Enter text here...";
AcceptsReturn = false;
TextWrapping = TextWrapping.NoWrap;
}
public bool AcceptsReturn
{
get { return (bool) GetValue(AcceptsReturnProperty); }
set
{
SetValue(AcceptsReturnProperty, value);
OnPropertyChanged();
}
}
public ICommand ClearTextCommand
{
get { return clearTextCommand ?? (clearTextCommand = new RelayCommand<object>(p => ClearText())); }
}
public bool IsClearButtonVisible
{
get { return (bool) GetValue(IsClearButtonVisibleProperty); }
set
{
SetValue(IsClearButtonVisibleProperty, value);
OnPropertyChanged();
}
}
public string Text
{
get { return (string) GetValue(TextProperty); }
set
{
SetValue(TextProperty, value);
OnPropertyChanged();
}
}
public string NullText
{
get { return (string) GetValue(NullTextProperty); }
set
{
SetValue(NullTextProperty, value);
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void ClearText()
{
Text = string.Empty;
tbInput.Focus();
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
and the converter used:
internal class DeleteButtonMultiConverter : MarkupExtension, IMultiValueConverter
{
private static DeleteButtonMultiConverter converter;
public DeleteButtonMultiConverter()
{
}
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values != null && values.Length == 2 && values[0] is bool && values[1] is string)
{
if ((bool) values[0] && !string.IsNullOrEmpty((string) values[1]))
return Visibility.Visible;
return Visibility.Collapsed;
}
return values;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return converter ?? (converter = new DeleteButtonMultiConverter());
}
}
Change the TextBlock inside your Hyperlink for a Run, like this (note that, since Run doesn't support VerticalAlignment or Margin, I've either removed or moved those properties):
<TextBlock Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="1,1,2,1">
<TextBlock.Visibility>
<MultiBinding Converter="{converter:DeleteButtonMultiConverter}">
<Binding ElementName="nullTextTextBox" Path="IsClearButtonVisible" Mode="OneWay" UpdateSourceTrigger="PropertyChanged"/>
<Binding ElementName="nullTextTextBox" Path="Text" Mode="OneWay" UpdateSourceTrigger="PropertyChanged"/>
</MultiBinding>
</TextBlock.Visibility>
<Hyperlink TextDecorations="{x:Null}" Command="{Binding ElementName=nullTextTextBox, Path=ClearTextCommand, Mode=OneWay}"
Focusable="False" >
<Run FontFamily="Wingdings 2" Text="Î" Foreground="Red" FontWeight="Bold" FontSize="14" />
</Hyperlink>
</TextBlock>

Binding SelectedItems in ListView to a ViewModel in Windows Phone 8.1

I have the following code:
<ListView SelectionMode="Multiple" ItemsSource="{Binding MyList}" ItemTemplate="{StaticResource MyListTemplate}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
With the following DataTemplate:
<Page.Resources>
<!-- Data Template for the ListView -->
<DataTemplate x:Key="MyListTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding Path=Icon}" />
<StackPanel Grid.Column="1" Orientation="Vertical">
<TextBlock Text="{Binding Path=EntryDate}" TextAlignment="Left" />
<TextBlock Text="{Binding Path=Url}" TextAlignment="Left" />
<TextBlock Text="{Binding Path=Text}" TextAlignment="Left" />
</StackPanel>
</Grid>
</DataTemplate>
</Page.Resources>
In my ViewModel I have the following:
private ObservableCollection<MyModel> myList;
public ObservableCollection<MyModel> MyList {
get { return myList; }
set {
myList = value;
RaisePropertyChanged("MyList");
}
}
public IEnumerable<MyModel> SelectedItems {
get { return MyList == null ? null : MyList.Where(e => e.IsSelected); }
}
And in my Model I have among others, my IsSelected property:
private bool isSelected;
public bool IsSelected {
get { return isSelected; }
set { Set(ref isSelected, value); }
}
I can see that the SelectedItems has all the elements that MyList has, however, when I select a few in the UI, the property IsSelected is not updated, they all remain false.
So what am I doing wrong here?
Thanks to YossiStarz in MSDN Forum, I managed to solve my problem. So here's his solution:
The problem is that you cant use Style to SetBinding on the element
that you put the style on. This is because the style is created once
when the listview is created and not for each of the item containers.
what you actualy did, is creating a style object that have a setter
object that it's Value property is bounded to the IsSelected of the
DataContext of the Style parent (witch it doesn't have). This binding
occurs to set the value of the Value property in the setter. If it
would have succeed to get value, this was the value it would set to
all of the items containers. I have a solution for you. First
and the simplest, create this helper class:
public class Helper {
public static string GetIsSelectedContainerBinding(DependencyObject obj) {
return (string)obj.GetValue(IsSelectedContainerBindingProperty);
}
public static void SetIsSelectedContainerBinding(DependencyObject obj, string value) {
obj.SetValue(IsSelectedContainerBindingProperty, value);
}
// Using a DependencyProperty as the backing store for IsSelectedContainerBinding. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsSelectedContainerBindingProperty =
DependencyProperty.RegisterAttached("IsSelectedContainerBinding", typeof(string), typeof(helper), new PropertyMetadata(null, IsSelectedContainerBindingPropertyChangedCallback));
public static void IsSelectedContainerBindingPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) {
BindingOperations.SetBinding(d, ListViewItem.IsSelectedProperty, new Binding() {
Source = d,
Path = new PropertyPath("Content." + e.NewValue),
Mode = BindingMode.TwoWay
});
}
}
Now change the setter to be like this:
<Style TargetType="ListViewItem">
<Setter Property="local:Helper.IsSelectedContainerBinding" Value="IsSelected"/>
</Style>
This should apply SetBinding to each container that is created.

DependencyProperty for ItemsSource

I have a XAML like:
<UserControl x:Class="Book.CustomControls.HeaderedComboBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Width="200" Height="50">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Margin="2,2" VerticalAlignment="Center" Text="{Binding Header}" FontWeight="{Binding HeaderFontWeight}"/>
<ComboBox Grid.Row="1" Margin="2,2" VerticalAlignment="Center" ItemsSource="{Binding ItemsSource, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</UserControl>
The cs-file therefor is:
public partial class HeaderedComboBox : UserControl
{
public HeaderedComboBox()
{
this.InitializeComponent();
this.HeaderFontWeight = FontWeights.Normal;
}
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register("Header", typeof(string), typeof(HeaderedComboBox));
public static readonly DependencyProperty HeaderFontWeightProperty = DependencyProperty.Register("HeaderFontWeight", typeof(FontWeight), typeof(HeaderedComboBox));
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(HeaderedComboBox));
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(object), typeof(HeaderedComboBox));
public string Header
{
get { return (string)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
public FontWeight HeaderFontWeight
{
get { return (FontWeight)GetValue(HeaderFontWeightProperty); }
set { SetValue(HeaderFontWeightProperty, value); }
}
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
}
I tried to use this UserControl in any Window with
<customControls:HeaderedComboBox Header="Category" ItemsSource="{Binding Categories}"/>
Categories is just an ObservableCollection<string>.
Unfortunately I don't see any items in the ComboBox. At the moment I have no idea, what I'm doing wrong. Any ideas?
System.Windows.Data Error: 40 : BindingExpression path error: 'Categories' property not found on 'object' ''HeaderedComboBox' (Name='')'. BindingExpression:Path=Categories; DataItem='HeaderedComboBox' (Name=''); target element is 'HeaderedComboBox' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
Your DataContext is set to HeaderedCombobox and there system is looking for a Categories. Property Header worked since you put a text into it without binding. Possible solution
<Window x:Class="WpfApplication5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:local="clr-namespace:WpfApplication5" Name="window">
<Grid>
<local:HeaderedComboBox Header="Category" ItemsSource="{Binding ElementName=window, Path=DataContext.Categories}"/>
</Grid>

How to specify the "Mode" and "UpdateSourceTrigger" in my "CustomControl's" markup?

Have a have CustomControl that consists of a ComboBox and a Button.
The binding works fine, but the Set only happens on LostFocus it seems.
This is my CustomControl's markup:
<Style TargetType="{x:Type cba:CustomComboBoxEditView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type cba:CustomComboBoxEditView}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<dxe:ComboBoxEdit x:Name="ComboBoxEdit"
Height="Auto"
Width="Auto"
Grid.Column="0"
ImmediatePopup="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ImmediatePopup}"
AutoComplete="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=AutoComplete}"
IsEnabled="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsEnabled}"
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Visibility}"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ItemsSource}"
DisplayMember="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DisplayMember}"
ValueMember="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ValueMember}"
EditValue="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EditValue}"/>
<dxe:ButtonEdit ShowText="False"
Grid.Column="1"
AllowDefaultButton="False"
IsEnabled="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsEnabled}"
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Visibility}">
<dxe:ButtonInfo x:Name="PART_btnView" GlyphKind="Search" ToolTipService.ToolTip="View" />
</dxe:ButtonEdit>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
For a normal ComboBoxEdit. the binding would look as follow:
EditValue="{Binding Path=SomePath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
This is what my markup looks like when i use the CustomControl:
<cba:CustomComboBoxEditView Name="someName"
ItemsSource="{Binding Path=SomeSource, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
DisplayMember="DisplayMember"
ValueMember="ValueMember"
EditValue="{Binding Path=SomePath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=False}"/>
How to specify the "Mode" and "UpdateSourceTrigger" in my CustomControl's markup?
What am i doing wrong in my CustomControl for the Set to only happen on LostFocus?
EDIT:
Code behind for CustomControl:
public class CustomComboBoxEditView : Control
{
#region Fields
public static readonly DependencyProperty ImmediatePopupProperty;
public static readonly DependencyProperty AutoCompleteProperty;
public static readonly DependencyProperty ItemsSourceProperty;
public static readonly DependencyProperty DisplayMemberProperty;
public static readonly DependencyProperty ValueMemberProperty;
public static readonly DependencyProperty EditValueProperty;
public static readonly DependencyProperty SystemAppEntityViewCodeProperty;
#endregion
#region Constructor
/// <summary>
/// Default constructor.
/// </summary>
static CustomComboBoxEditView()
{
// Initialize as lookless control
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomComboBoxEditView), new FrameworkPropertyMetadata(typeof(CustomComboBoxEditView)));
// Initialize dependency properties
ImmediatePopupProperty = DependencyProperty.Register("ImmediatePopup",
typeof(bool), typeof(CustomComboBoxEditView), new UIPropertyMetadata(null));
AutoCompleteProperty = DependencyProperty.Register("AutoComplete", typeof(bool),
typeof(CustomComboBoxEditView), new UIPropertyMetadata(null));
ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(object),
typeof(CustomComboBoxEditView), new UIPropertyMetadata(null));
DisplayMemberProperty = DependencyProperty.Register("DisplayMember", typeof(string),
typeof(CustomComboBoxEditView), new UIPropertyMetadata(null));
ValueMemberProperty = DependencyProperty.Register("ValueMember", typeof(string),
typeof(CustomComboBoxEditView), new UIPropertyMetadata(null));
EditValueProperty = DependencyProperty.Register("EditValue", typeof(object),
typeof(CustomComboBoxEditView), new UIPropertyMetadata(null));
SystemAppEntityViewCodeProperty = DependencyProperty.Register("SystemAppEntityViewCode", typeof(string),
typeof(CustomComboBoxEditView), new UIPropertyMetadata(null));
}
#endregion
#region Custom Control Properties
public bool ImmediatePopup
{
get { return (bool)GetValue(ImmediatePopupProperty); }
set { SetValue(ImmediatePopupProperty, value); }
}
public bool AutoComplete
{
get { return (bool)GetValue(AutoCompleteProperty); }
set { SetValue(AutoCompleteProperty, value); }
}
public object ItemsSource
{
get { return (object)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public string DisplayMember
{
get { return (string)GetValue(DisplayMemberProperty); }
set { SetValue(DisplayMemberProperty, value); }
}
public string ValueMember
{
get { return (string)GetValue(ValueMemberProperty); }
set { SetValue(ValueMemberProperty, value); }
}
public object EditValue
{
get { return (object)GetValue(EditValueProperty); }
set { SetValue(EditValueProperty, value); }
}
public string SystemAppEntityViewCode
{
get { return (string)GetValue(SystemAppEntityViewCodeProperty); }
set { SetValue(SystemAppEntityViewCodeProperty, value); }
}
#endregion
}
Found a work around:
Register for the PopupClosed Event and then set the Property.
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var btnComboBoxEdit = Template.FindName("ComboBoxEdit", this) as ComboBoxEdit;
btnComboBoxEdit.PopupClosed +=new ClosePopupEventHandler(btnComboBoxEdit_PopupClosed);
}
private void btnComboBoxEdit_PopupClosed(object sender, ClosePopupEventArgs e)
{
EditValue = e.EditValue;
}

Categories

Resources