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;
}
Related
I just want make some of my custom control for the render performance.
And Bind my Data
making custom control is OK, but i can't set dependancyProperty for data bind.
I made a Custom Control File
CustomCotrol1.cs
public class CustomControl1 : Control
{
static CustomControl1()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));
}
public string MyProperty
{
get { return (string)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(string), typeof(CustomControl1), new PropertyMetadata(0));
}
And set my Generic.xml
<Style TargetType="{x:Type views:CustomControl1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type views:CustomControl1}">
<Button
Width="50"
Height="50"
Content="{TemplateBinding MyProperty}"
>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And use it in my view
<ListBox x:Name="TestListBox" Grid.Row="0"
ItemsSource="{Binding DataList}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<RichTextBox
Grid.Row="0"
TextChanged="rtb_TextChanged"
HorizontalAlignment="Left"
>
<RichTextBox.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="TextAlignment" Value="Right"/>
<Setter Property="Margin" Value="0" />
</Style>
</RichTextBox.Resources>
<FlowDocument>
<Paragraph>test <Bold>test111</Bold></Paragraph>
</FlowDocument>
</RichTextBox>
<local:CustomControl1
MyProperty="{Binding MSG}"
/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
also it is my viewmodel
public class TestListViewModel : ViewModelBase
{
private ObservableCollection<TestMsgModel> _dataList = new ObservableCollection<TestMsgModel>();
public ObservableCollection<TestMsgModel> DataList
{
get => _dataList;
set
{
_dataList = value;
OnPropertyChanged("DataList");
}
}
public void OnPropertyChanged(string PropertyName)
{
if (PropertyChangedHandler != null)
{
PropertyChangedHandler(this, new PropertyChangedEventArgs(PropertyName));
}
}
public TestListViewModel()
{
TestMsgModel testMsgModel = new TestMsgModel();
testMsgModel.MSG = "TEST MSG 1";
DataList.Add(testMsgModel);
}
}
When i remove the DependencyProperty in CustomControl, it works!
But if not remove, there are some error.
How do i???
Thank you, Clemens
I just changed "new PropertyMetadata(0));" to "new PropertyMetadata(null));"
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(string), typeof(CustomControl1), new PropertyMetadata(0));
Change to
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(string), typeof(CustomControl1), new PropertyMetadata(null));
It is solved.
I am creating Custom User Control. My goal is to make the control reusable.
I am using ItemsControl, here is XAML
<ItemsControl ItemsSource="{Binding Path=ItemSource , RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls:MyItemsControlWithButtons}}}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type Type=typeOf(DataTemplateType)????}">
<ContentControl x:Name="instruction" Content="{Binding Path=DataTemplateControl, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls:MyItemsControlWithButtons}}}"/>
<DataTemplate.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="#DDDDDD" TargetName="instruction" />
</Trigger>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="#EEEEEE" TargetName="instruction" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here is control:MyItemsControlWithButtons
public partial class DraggableItemsControlWithButtons : UserControl
{
public DraggableItemsControlWithButtons()
{
InitializeComponent();
}
public static readonly DependencyProperty DataTemplateTypeProperty =
DependencyProperty.Register(nameof(DataTemplateType), typeof(Type), typeof(DraggableItemsControlWithButtons), new UIPropertyMetadata(null));
public Type DataTemplateType
{
get { return (Type)GetValue(DataTemplateTypeProperty); }
set { SetValue(DataTemplateTypeProperty, value); }
}
public static readonly DependencyProperty ItemSourceProperty =
DependencyProperty.Register(nameof(ItemSource), typeof(IEnumerable), typeof(DraggableItemsControlWithButtons), new UIPropertyMetadata(null));
public IEnumerable ItemSource
{
get { return (IEnumerable)GetValue(ItemSourceProperty); }
set { SetValue(ItemSourceProperty, value); }
}
public static readonly DependencyProperty DataTemplateControlProperty =
DependencyProperty.Register(nameof(DataTemplateControl), typeof(Control), typeof(DraggableItemsControlWithButtons), new UIPropertyMetadata(null));
public Control DataTemplateControl
{
get { return (Control)GetValue(DataTemplateControlProperty); }
set { SetValue(DataTemplateControlProperty, value); }
}
}
As you can see I want to bind/set DataTemplateType to DataType of the ItemsControl.ItemTemplate's DataTemplate
How can I achieve it?
Thank you
You could probaby simplify your control by replacing the DataTemplateControl property by an ItemTemplate property. And to complete the similarity with an ItemsControl, rename the ItemSource property to ItemsSource.
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(
nameof(ItemsSource), typeof(IEnumerable),
typeof(DraggableItemsControlWithButtons));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemTemplateProperty =
DependencyProperty.Register(
nameof(ItemTemplate), typeof(DataTemplate),
typeof(DraggableItemsControlWithButtons));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemSourceProperty); }
set { SetValue(ItemSourceProperty, value); }
}
The ItemsControl in the UserControl's XAML would look like this:
<ItemsControl
ItemsSource="{Binding ItemsSource,
RelativeSource={RelativeSource AncestorType=UserControl}}"
ItemTemplate="{Binding ItemTemplate,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
You would then assign the ItemTemplate just like in any other control with an ItemTemplate property:
<local:DraggableItemsControlWithButtons ItemSource="{Binding ...}">
<local:DraggableItemsControlWithButtons.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</local:DraggableItemsControlWithButtons.ItemTemplate>
</local:DraggableItemsControlWithButtons>
I have created several UserControls like a HeaderedDatePicker.
The XAML of this UserControl looks like:
<UserControl x:Class="Book.CustomControls.HeaderedDatePicker"
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}"/>
<DatePicker Grid.Row="1" Margin="2,2" VerticalAlignment="Center" SelectedDate="{Binding Date, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</UserControl>
The Code-Behind with the registered DependencyPropertys is:
public partial class HeaderedDatePicker : UserControl
{
public HeaderedDatePicker()
{
this.InitializeComponent();
this.HeaderFontWeight = FontWeights.Normal;
this.Date = DateTime.Now;
}
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register("Header", typeof(string), typeof(HeaderedDatePicker));
public static readonly DependencyProperty HeaderFontWeightProperty = DependencyProperty.Register("HeaderFontWeight", typeof(FontWeight), typeof(HeaderedDatePicker));
public static readonly DependencyProperty DateProperty = DependencyProperty.Register("Date", typeof(DateTime), typeof(HeaderedDatePicker));
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 DateTime Date
{
get { return (DateTime)GetValue(DateProperty); }
set { SetValue(DateProperty, value); }
}
}
Everything is working fine. Now I want to create another HeaderedControl like a HeaderedComboBox or so. My question now is:
Do I have to write the HeaderProperty and HeaderFontWeightProperty in each Code-Behind-File or is there a way to do this in a base-class?
I tried to create a base-class where the properties are registered, but in the code-behind of the HeaderedDatePicker I couldn't inherit from my class
public class main : UserControl
{
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register("Header", typeof(string), typeof(main));
public static readonly DependencyProperty HeaderFontWeightProperty = DependencyProperty.Register("HeaderFontWeight", typeof(FontWeight), typeof(main));
public static readonly DependencyProperty DateProperty = DependencyProperty.Register("Date", typeof(DateTime), typeof(main));
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 DateTime Date
{
get { return (DateTime)GetValue(DateProperty); }
set { SetValue(DateProperty, value); }
}
}
Then in every UserControl you are supposed to derive from previously declared main.
public partial class HeaderedComboBox : main
{
public HeaderedComboBox()
{
this.InitializeComponent();
this.DataContext = this;
}
}
Finally
<local:main x:Class="WpfApplication5.HeaderedComboBox"
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:local ="clr-namespace:WpfApplication5"
mc:Ignorable="d"
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>
Let me know whether it works.
You can register attached property. See this link. Attached property can be used for any dependency object.
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.
I created the following xaml:
<Button x:Name="PriceButton">
<Button.Template>
<ControlTemplate>
<Border x:Name="ButtonBorder"
CornerRadius="2"
Background="{StaticResource DarkReflectionBrush}"
BorderBrush="Black"
BorderThickness="1" HorizontalAlignment="Stretch">
<ContentPresenter
VerticalAlignment="Center"
HorizontalAlignment="Left">
<ContentPresenter.Content>
<Grid x:Name="ContentGrid" HorizontalAlignment="Left" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="15*" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="Price" Grid.Column="0" FontFamily="Calibri" FontSize="8"
VerticalAlignment="Bottom" Text="{Binding Path=PriceText}"
Foreground="{Binding Path=PriceColor}" ></TextBlock>
<TextBlock x:Name="Main" Grid.Column="1" FontFamily="Calibri" FontSize="14"
Text="{Binding Path=MainText}"
Foreground="{Binding Path=MainTextColor}" />
<TextBlock x:Name="Vol" Grid.Column="2" FontFamily="Calibri" FontSize="8"
VerticalAlignment="Bottom" Text="{Binding Path=VolatilityText}"
Foreground="{Binding Path=VolatilityColor}" />
</Grid>
</ContentPresenter.Content>
</ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Button.IsPressed" Value="true">
<Setter TargetName="ButtonBorder" Property="Background" Value="{StaticResource PressedBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
Here's the Code Behind:
public static readonly DependencyProperty PriceTextProperty =
DependencyProperty.Register(
"PriceText",
typeof (string),
typeof (LivePriceVolButton),
new FrameworkPropertyMetadata(string.Empty, OnPriceTextChanged));
public string PriceText {
get {
return (string)GetValue(PriceTextProperty);
}
set {
SetValue(PriceTextProperty, value);
}
}
public static readonly DependencyProperty PriceColorProperty =
DependencyProperty.Register(
"PriceColor",
typeof (Color),
typeof (LivePriceVolButton),
new FrameworkPropertyMetadata(Colors.White));
public Color PriceColor {
get {
return (Color) GetValue(PriceColorProperty);
}
set {
SetValue(PriceColorProperty, value);
}
}
public static readonly DependencyProperty MainTextProperty =
DependencyProperty.Register(
"MainText",
typeof (string),
typeof (LivePriceVolButton),
new FrameworkPropertyMetadata(string.Empty));
public string MainText {
get {
return (string) GetValue(MainTextProperty);
}
set {
SetValue(MainTextProperty, value);
}
}
public static readonly DependencyProperty MainTextColorProperty =
DependencyProperty.Register(
"MainTextColor",
typeof(Color),
typeof(LivePriceVolButton),
new FrameworkPropertyMetadata(Colors.White));
public Color MainTextColor {
get {
return (Color) GetValue(MainTextColorProperty);
}
set {
SetValue(MainTextColorProperty, value);
}
}
public static readonly DependencyProperty VolatilityTextProperty =
DependencyProperty.Register(
"VolatilityText",
typeof(string),
typeof(LivePriceVolButton),
new FrameworkPropertyMetadata(string.Empty));
public string VolatilityText {
get {
return (string) GetValue(VolatilityTextProperty);
}
set {
SetValue(VolatilityTextProperty, value);
}
}
public static readonly DependencyProperty VolatilityColorProperty =
DependencyProperty.Register(
"VolatilityColor",
typeof(Color),
typeof(LivePriceVolButton),
new FrameworkPropertyMetadata(Colors.White));
public Color VolatilityColor {
get {
return (Color) GetValue(VolatilityColorProperty);
}
set {
SetValue(VolatilityColorProperty, value);
}
}
When I insert my user control onto a form like this
<my:LivePriceVolButton Margin="43.03,0,0,32" x:Name="livePriceVolButton1"
xmlns:my="clr-namespace:MultiTextBlockButton" HorizontalAlignment="Left"
Width="91.703" Height="30" VerticalAlignment="Bottom"
MainText="MID" MainTextColor="LightBlue" PriceColor="Red" PriceText="1234.56"
VolatilityText="12.2%" VolatilityColor="Aqua" />
I don't see anything in the button at all. Any ideas?
Thanks
You have to set the DataContext for the Button to be equal to the parent UserControl for your Bindings to work. Try something like this:
<UserControl x:Name="uc" ...>
<Button x:Name="PriceButton" DataContext="{Binding ElementName=uc}">
<!--Other code here...-->
</Button>
</UserControl>
I also see that you're using "Color" as the Type for some of your DependencyProperties. I suggest you change them to "Brush" instead. Otherwise the related bindings (e.g. Foreground="{Binding VolatilityColor}") won't work.