Combobox usercontrol SelectedItem data binding in WPF container - wpf

This is my combobox usercontrol:
<UserControl x:Class="Hexa.Screens.UsrColorPicker"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Hexa.Screens"
xmlns:sys="clr-namespace:System;assembly=mscorlib" Height="40" Width="200" Name="uccolorpicker"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<ObjectDataProvider MethodName="GetType" ObjectType="{x:Type sys:Type}" x:Key="colorsTypeOdp">
<ObjectDataProvider.MethodParameters>
<sys:String>System.Windows.Media.Colors, PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</sys:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ObjectDataProvider ObjectInstance="{StaticResource colorsTypeOdp}" MethodName="GetProperties" x:Key="colorPropertiesOdp"/>
</ResourceDictionary>
</UserControl.Resources>
<ComboBox Name="superCombo" ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}" SelectedValuePath="Name" SelectedValue="{Binding ElementName=uccolorpicker, Path=SelectedColor}" Text="{Binding ElementName=uccolorpicker,Path=Text}" SelectedItem="{Binding ElementName=uccolorpicker, Path=SelectedItem}" SelectedIndex="{Binding ElementName=uccolorpicker, Path=SelectedIndex}" SelectionChanged="superCombo_SelectionChanged" HorizontalContentAlignment="Stretch" >
<ComboBox.ItemTemplate>
<DataTemplate >
<WrapPanel Orientation="Horizontal" Background="Transparent">
<TextBlock Width="20" Height="20" Margin="5" Background="{Binding Name}" />
<TextBlock Text="{Binding Name}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="14" FontStyle="Italic" FontWeight="Bold" FontFamily="Palatino Linotype" />
</WrapPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
This is the code behind:
namespace Hexa.Screens
{
/// <summary>
/// Interaction logic for UsrColorPicker.xaml
/// </summary>
public partial class UsrColorPicker : UserControl
{
public UsrColorPicker()
{
InitializeComponent();
}
public Brush SelectedColor
{
get { return (Brush)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}
public int SelectedIndex
{
get { return (int)GetValue(SelectedIndexProperty); }
set { SetValue(SelectedIndexProperty, value); }
}
public int SelectedItem
{
get { return (int)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public string Text
{
get { return (string)GetValue(SelectedTextProperty); }
set { SetValue(SelectedTextProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedColor. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedColorProperty =
DependencyProperty.Register("SelectedColor", typeof(Brush), typeof(UsrColorPicker), new UIPropertyMetadata(null));
public static readonly DependencyProperty SelectedIndexProperty =
DependencyProperty.Register("SelectedIndex", typeof(int), typeof(UsrColorPicker), new UIPropertyMetadata(null));
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(int), typeof(UsrColorPicker), new UIPropertyMetadata(null));
public static readonly DependencyProperty SelectedTextProperty =
DependencyProperty.Register("Text", typeof(int), typeof(UsrColorPicker), new UIPropertyMetadata(null));
public static readonly RoutedEvent SettingConfirmedEvent =
EventManager.RegisterRoutedEvent("SettingConfirmedEvent", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(UsrColorPicker));
public event RoutedEventHandler SettingConfirmed
{
add { AddHandler(SettingConfirmedEvent, value); }
remove { RemoveHandler(SettingConfirmedEvent, value); }
}
private void superCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(UsrColorPicker.SettingConfirmedEvent));
}
}
}
I am trying to set its SelectedItem,SelectedIndex thru xaml binding in my container's XAML as under:-
<local:UsrColorPicker x:Name="cmbItem_Group_back_color" HorizontalAlignment="Center" Width="205" Height="22" SettingConfirmed="cmbItem_Group_back_color_SettingConfirmed" SelectedColor ="{Binding Path=CurrentRec.Primary_Tone,Mode=TwoWay}" Canvas.Left="97" Canvas.Top="92" />
The code behind is as under:-
form_load()
{
this.DataContext = DataContract_ButtonSettings;
}
But the selecteditem's text is not showing on the combobox as it should.

I found the solution.It was a careless mistake..
The bug was nowhere in the code posted..Actually,i was using the usercontrol's selectedcolor property to bind to the backcolor property of an element in the view.The viewmodel updates the SelectedColor property of the ColorCombobox.The viewmodel was being updated from many places in the code behind.And at one place,the viewmodel's SelectedColor property was being set with the HEX equivalent of the known Color of System.Windows.Media.Colors Known color,and when the view model wud try to bind to that Hex element to the ComboBox,it did not find any matching entry for that Hex value in the dropdown list,and hence,though the background color of the control was being effected,the combobox text showed blank.:-).
anyways,solved it...thanks for your time Ed. :-).

Related

Bind view models' property to dependency property

I have created reusable components let's say a label and a textbox:
HeaderAndTextBox.xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<TextBlock
Margin="10,0,0,0"
FontSize="16"
FontWeight="DemiBold"
Foreground="White"
Text="{Binding Header, ElementName=root}" />
<TextBox
Grid.Row="1"
MaxWidth="300"
Margin="10"
mah:TextBoxHelper.ClearTextButton="True"
mah:TextBoxHelper.IsWaitingForData="True"
FontSize="16"
Text="{Binding TextBoxContent, ElementName=root}" />
</Grid>
Now as you can see I created dependency properties for the Text properties. Here is the code behind:
HeaderAndTextBox.xaml.cs
public partial class HeaderAndTextBox : UserControl
{
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(string), typeof(HeaderAndTextBox), new PropertyMetadata(string.Empty));
public string Header
{
get { return (string)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
public static readonly DependencyProperty TextBoxContentProperty =
DependencyProperty.Register("TextBoxContent", typeof(string), typeof(HeaderAndTextBox), new PropertyMetadata(string.Empty));
public string TextBoxContent
{
get { return (string)GetValue(TextBoxContentProperty); }
set { SetValue(TextBoxContentProperty, value); }
}
public HeaderAndTextBox()
{
InitializeComponent();
}
}
In my view I use this reusable component like this:
MyView.xaml
<controls:HeaderAndTextBox
Grid.Row="1"
Margin="10,10,0,0"
Header="Last Name"
TextBoxContent="{Binding Path=LastName, UpdateSourceTrigger=PropertyChanged}" />
And my view model:
MyViewModel.cs
private string? _lastName;
public string? LastName
{
get
{
return _lastName;
}
set
{
_lastName = value;
OnPropertyChanged(nameof(LastName));
}
}
Question is, how can I bind this dependency property to my view model's property? As my approach doesn't work. I have more than one property so I must find a solution for the binding to be dynamic.
Could it be that for this kind of problem, I should use a completely different approach?
The internal elements must bind to the control's properties either by Binding.ElementName, where the the named UserControl is the binding source or by using Binding.RelativeSource.
HeaderAndTextBox.xaml
<UserControl>
<TextBox Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=TextBoxContent, UpdateSourceTrigger=PropertyChanged}" />
</UserControl>
Next, make sure the DataContext of the parent element that hosts HeaderAndTextBox is correct:
MainWindow.xaml
<Window>
<Window.DataContext>
<MyViewModel />
</Window.DataContext>
<StackPanel>
<!-- The HeaderAndTextBox inherits the parent's DataContext,
which is MyViewModel, automatically. -->
<HeaderAndTextBox TextBoxContent="{Binding SomeMyViewModelTextProperty}" />
<Grid DataContext="{Binding GridViewModel}">
<!-- Same control, different instance,
binds to a different view model class (GridViewModel). -->
<HeaderAndTextBox TextBoxContent="{Binding SomeGridViewModelTextProperty}" />
</Grid>
</StackPanel>
</Window>
To make the HeaderAndTextBox.TextBoxContent property send data back to the view model automatically (when typing into the TextBox), you should configure the dependency property accordingly by using a FrameworkPropertyMetadata object instead of a PropertyMetadata and set the FrameworkPropertyMetadata.BindsTwoWayByDefault property:
HeaderAndTextBox.xaml.cs
partial class HeaderAndTextBox : UserControl
{
public static readonly DependencyProperty TextBoxContentProperty = DependencyProperty.Register(
"TextBoxContent",
typeof(string),
typeof(HeaderAndTextBox),
new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string TextBoxContent
{
get => (string)GetValue(TextBoxContentProperty);
set => SetValue(TextBoxContentProperty, value);
}
}

How to set a proper context for a ListBox ItemTemplate property in a custom user control

Here is my ListBox Custom control: UCListBox. The MainLabel is the property I have issues with. It is used as a "label" for a ListBox item:
<ListBox ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=local:UCListBox}}"
SelectedItem="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType=local:UCListBox}}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding MainLabel, RelativeSource={RelativeSource AncestorType=local:UCListBox}}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
CodeBehind:
public partial class UCListBox : UserControl
{
public UCListBox()
{
InitializeComponent();
}
public object ItemsSource
{
get { return (object)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(object), typeof(UCListBox), new PropertyMetadata(null));
public object SelectedItem
{
get { return (object)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedItem. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(UCListBox), new PropertyMetadata(null));
public string MainLabel
{
get { return (string)GetValue(MainLabelProperty); }
set { SetValue(MainLabelProperty, value); }
}
// Using a DependencyProperty as the backing store for MainLabel. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MainLabelProperty =
DependencyProperty.Register("MainLabel", typeof(string), typeof(UCListBox), new PropertyMetadata(string.Empty));
}
Here I try to use my custom control in a window:
<local:UCListBox
ItemsSource="{Binding Participants}"
SelectedItem="{Binding SelectedParticipant}"
MainLabel ="{Binding NameShort1}"
/>
I get a binding error:
Property "NameShort1 is not found for WindowEditCaseVM.
The context of the MainLabel property is my View Model like the rest of the properties and not the context of a ListBox item. How do I fix the context of my custom control's property to display listbox items correctly?
The thing that helped me to solve my problem with help of #Clemens was to make the DataTemplate into a resource and define a DataType property for it.
First, define a namespace, containing my Participant class in the head of my UserControl:
xmlns:model="clr-namespace:DocConstructor.BusinessModel"
Then define the resource
<UserControl.Resources>
<DataTemplate DataType="{x:Type model:Participant}">
<StackPanel>
<TextBlock Text="{Binding NameShort1}"/>
<TextBlock Text="{Binding CaseStatusType.Description}"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
Full code:
<UserControl x:Class="DocConstructor.UserControls.UCListBox"
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:DocConstructor.UserControls"
mc:Ignorable="d"
xmlns:model="clr-namespace:DocConstructor.BusinessModel"
>
<UserControl.Resources>
<DataTemplate DataType="{x:Type model:Participant}">
<StackPanel>
<TextBlock Text="{Binding NameShort1}"/>
<TextBlock Text="{Binding CaseStatusType.Description}"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<ListBox ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=local:UCListBox}}"
SelectedItem="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType=local:UCListBox}}"
>
</ListBox>
</UserControl>

Binding from ItemsControl DataTemplate to parent UserControls DependencyProperty

I want to create an user control which contains ItemsControl with buttons and I want to bind theirs contents to user controls dependency property (something like DisplayMemberPath property).
My xaml.cs code:
public partial class ButtonsItemsSourceControl : UserControl
{
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(object), typeof(ButtonsItemsSourceControl), null);
public static readonly DependencyProperty DisplayMemberProperty = DependencyProperty.Register("DisplayMember", typeof(string), typeof(ButtonsItemsSourceControl), null);
public object ItemsSource
{
get { return (ICollection<object>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public string DisplayMember
{
get { return (string)GetValue(DisplayMemberProperty); }
set { SetValue(DisplayMemberProperty, value); }
}
public ButtonsItemsSourceControl()
{
InitializeComponent();
}
}
My xaml code:
<UserControl x:Class="Controls.ButtonsItemsSourceControl"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480" x:Name="root">
<ItemsControl x:Name="ctrl" ItemsSource="{Binding ItemsSource, ElementName=root}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content= ?????/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
What binding expression should I write in Content property to do it ?
Just for clarity, are you asking how to bind a child property to a parent property?
RelativeSource

DataTemplate inside HierarchicalDataTemplate

I needed to build a custom treeview as a user control. I called it for the sake of the example TreeViewEx :
<UserControl x:Class="WpfApplication4.TreeViewEx"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="root">
<Grid>
<TreeView ItemsSource="{Binding Path=ItemsSource, ElementName=root}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Node : "/>
<ContentControl Content="{Binding Path=AdditionalContent, ElementName=root}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</UserControl>
The idea is to have a fixed part of the content of the ItemTemplate and a customizable part of it.
Of course, I created two dependency properties on the TreeViewEx class :
public partial class TreeViewEx
{
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
"ItemsSource", typeof(IEnumerable), typeof(TreeViewEx));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty AdditionalContentProperty = DependencyProperty.Register(
"AdditionalContent", typeof(object), typeof(TreeViewEx));
public object AdditionalContent
{
get { return GetValue(AdditionalContentProperty); }
set { SetValue(AdditionalContentProperty, value); }
}
public TreeViewEx()
{
InitializeComponent();
}
}
Having a simple node class like so :
public class Node
{
public string Name { get; set; }
public int Size { get; set; }
public IEnumerable<Node> Children { get; set; }
}
I would feed the treeview. I place an instance of TreeViewEx on the MainWindow of a WPF test project :
<Window x:Class="WpfApplication4.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:WpfApplication4">
<Grid>
<local:TreeViewEx x:Name="tree">
<local:TreeViewEx.AdditionalContent>
<TextBlock Text="{Binding Name}"/>
</local:TreeViewEx.AdditionalContent>
</local:TreeViewEx>
</Grid>
</Window>
And finally feed it :
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
var dummyData = new ObservableCollection<Node>
{
new Node
{
Name = "Root",
Size = 3,
Children = new ObservableCollection<Node>
{
new Node{
Name="Child1",
Size=2,
Children = new ObservableCollection<Node>{
new Node{
Name = "Subchild",
Size = 1
}
}
}
}
}
};
tree.ItemsSource = dummyData;
}
}
However it doesn't work as expected namely at first the ContentControl has the data but as I expand the nodes it does not display the ContentControl's content.
I don't really get it.. I should use a DataTemplate or something else?
The problem is that you're setting the content to an instance of a control, and that control can only have one parent. When you expand the tree and it adds it to the second node, it removes it from the first one.
As you suspected, you want to supply a DataTemplate to TreeViewEx instead of a control. You can use a ContentPresenter to instantiate the template at each level of the tree:
Replace the AdditionalContentProperty with:
public static readonly DependencyProperty AdditionalContentTemplateProperty = DependencyProperty.Register(
"AdditionalContentTemplate", typeof(DataTemplate), typeof(TreeViewEx));
public DataTemplate AdditionalContentTemplate
{
get { return (DataTemplate)GetValue(AdditionalContentTemplateProperty); }
set { SetValue(AdditionalContentTemplateProperty, value); }
}
change the HierarchicalDataTemplate in your UserControl's XAML to:
<StackPanel Orientation="Horizontal">
<TextBlock Text="Node : "/>
<ContentPresenter ContentTemplate="{Binding Path=AdditionalContentTemplate, ElementName=root}"/>
</StackPanel>
and change MainWindow to:
<local:TreeViewEx x:Name="tree">
<local:TreeViewEx.AdditionalContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</local:TreeViewEx.AdditionalContentTemplate>
</local:TreeViewEx>

UserControl Property Binding not Working

Given the following code why would "My Stupid Text" never be bound to the UserControls text box?
MainPage.xaml
<Grid x:Name="LayoutRoot">
<Local:Stupid StupidText="My Stupid Text" />
</Grid>
Stupid.xaml
<UserControl x:Class="SilverlightApplication5.Stupid"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock Text="{Binding StupidText}" />
</Grid>
</UserControl>
Stupid.xaml.cs
public partial class Stupid : UserControl
{
public string StupidText
{
get { return (string)GetValue(StupidTextProperty); }
set { SetValue(StupidTextProperty, value); }
}
// Using a DependencyProperty as the backing store for StupidText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StupidTextProperty =
DependencyProperty.Register("StupidText", typeof(string), typeof(Stupid), new PropertyMetadata(string.Empty));
public Stupid()
{
InitializeComponent();
}
}
Do the following in the constructor of your user control (after InitializeComponent) and your textblock should be aware of its datacontext:
this.DataContext = this;
Give your Stupid control a name:-
<Local:Stupid x:Name="MyStupid" StupidText="My Stupid Text" />
Then you can use element binding like this:-
<TextBlock Text="{Binding StupidText, ElementName=MyStupid}" />

Resources