WPF: how do I edit a label when bound values change? [duplicate] - c#

This question already has answers here:
XAML binding not working on dependency property?
(1 answer)
Callback when dependency property recieves xaml change
(2 answers)
Closed 9 months ago.
I'm trying to make a user control in WPF (using xceed). I have a combobox and an integerUpDown.
A label should change its content when one of those changes its value. First label has just a simple binding to the integerUpDown, that's working. Visual Studio created OnPropertyChanged() and getters/setters automatically and I try to use it to bind my result to the second label but it's not working. Nothing happens.
This is my XAML:
<UserControl x:Class="WpfApp2.CardSelectionControl"
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:WpfApp2" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel Margin="5" Width="300" Height="400" Background="LightGray">
<TextBox Margin="5" AcceptsReturn="True" FontSize="20" Padding="5" Height="70">placeholding</TextBox>
<ComboBox SelectedIndex="{Binding SelectedIndex}" Background="Red" Margin="5">
<ComboBoxItem Content="card1"></ComboBoxItem>
<ComboBoxItem Content="card2"></ComboBoxItem>
</ComboBox>
<xctk:UIntegerUpDown Margin="5" x:Name="upDown" Value="{Binding Level}" ></xctk:UIntegerUpDown>
<Label Height="50" Content="{Binding Level}"></Label>
<Label Height="50" Content="{Binding Result}" ></Label>
</StackPanel>
</UserControl>
Code behind:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApp2
{
public partial class CardSelectionControl : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int Level
{
get { OnPropertyChanged(); return (int)GetValue(LevelProperty); }
set
{
OnPropertyChanged();
SetValue(LevelProperty, value);
}
}
public int SelectedIndex
{
get { OnPropertyChanged(); return (int)GetValue(SelectedIndexProperty); }
set
{
OnPropertyChanged();
SetValue(SelectedIndexProperty, value);
}
}
public int Result
{
get { return (int)GetValue(ResultProperty); }
set { SetValue(ResultProperty, value); }
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (SelectedIndex == 0)
{ Result = Level * 2; }
else if (SelectedIndex == 1)
{ Result = Level * 3; }
}
public static readonly DependencyProperty ResultProperty =
DependencyProperty.Register("Result", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(20));
public static readonly DependencyProperty LevelProperty =
DependencyProperty.Register("Level", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(10));
public static readonly DependencyProperty SelectedIndexProperty =
DependencyProperty.Register("SelectedIndex", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(0));
public CardSelectionControl()
{
DataContext = this;
InitializeComponent();
}
}
}

I assume you expect your setters to get called, hence why you are calling OnPropertyChanged everywhere. They don't get called so your code won't be executed.
Instead, you can add a callback to your dependency properties so you know when the values get changed:
public static readonly DependencyProperty LevelProperty =
DependencyProperty.Register("Level",
typeof(int),
typeof(CardSelectionControl),
new PropertyMetadata(10, new PropertyChangedCallback(OnChanged)));
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var cardSelectionControl = (CardSelectionControl)d;
cardSelectionControl.Recalculate();
}
I took the liberty of changing OnPropertyChanged to Recalculate, added a PropertyChangedCallback for the combobox as well and the entire code becomes:
public partial class CardSelectionControl : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int Level
{
get { return (int)GetValue(LevelProperty); }
set { SetValue(LevelProperty, value); }
}
public int SelectedIndex
{
get { return (int)GetValue(SelectedIndexProperty); }
set { SetValue(SelectedIndexProperty, value); }
}
public int Result
{
get { return (int)GetValue(ResultProperty); }
set { SetValue(ResultProperty, value); }
}
public void Recalculate()
{
if (SelectedIndex == 0)
Result = Level * 2;
else if (SelectedIndex == 1)
Result = Level * 3;
}
public static readonly DependencyProperty ResultProperty =
DependencyProperty.Register("Result", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(20));
public static readonly DependencyProperty LevelProperty =
DependencyProperty.Register("Level", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(10, new PropertyChangedCallback(OnChanged)));
public static readonly DependencyProperty SelectedIndexProperty =
DependencyProperty.Register("SelectedIndex", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(0, new PropertyChangedCallback(OnChanged)));
private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var cardSelectionControl = (CardSelectionControl)d;
cardSelectionControl.Recalculate();
}
public CardSelectionControl()
{
DataContext = this;
InitializeComponent();
}
}

Related

Unable to bind properties using custom controls in UWP in a ListView

I have a ListView in UWP which displays a list of custom controls CustomControl. Reading around I have seen that other users have face similar issues and their solution mostly revolved around setting the DataContext of their controls, but I cannot understand how I can do that in my example. In order to dynamically update the view I used DependencyProperties in my model which is the following:
public class DataObject : DependencyObject
{
public string Name
{
get { return (string)GetValue(nameProperty); }
set { SetValue(nameProperty, value); }
}
// Using a DependencyProperty as the backing store for name. This enables animation, styling, binding, etc...
public static readonly DependencyProperty nameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(DataObject), new PropertyMetadata("Name"));
}
Then in my main page I implemented the following logic to change the Name of my third element:
public sealed partial class MainPage : Page
{
private readonly ObservableCollection<DataObject> dataList;
public MainPage()
{
this.InitializeComponent();
this.dataList = new ObservableCollection<DataObject>();
for (int i = 0; i < 5; i++)
{
DataObject dataObject = new DataObject();
dataObject.Name = "Item " + i.ToString();
this.dataList.Add(dataObject);
}
DataListView.ItemsSource = dataList;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var obj = dataList.ElementAt(2);
obj.Name = "Hello!";
}
}
The XAML for the main page is the following:
<Page
x:Class="ListViewTest.MainPage"
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:controls="using:ListViewTest.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<ListView Name="DataListView">
<ListView.ItemTemplate>
<DataTemplate>
<controls:CustomControl DisplayName="{Binding Name}"></controls:CustomControl>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Content="Button" HorizontalAlignment="Center" Height="92" Width="238"
Click="Button_Click"/>
</Grid>
</Page>
The custom control CustomControl is this:
namespace ListViewTest.Controls
{
public sealed partial class CustomControl : UserControl
{
public string DisplayName
{
get { return (string)GetValue(DisplayNameProperty); }
set {
SetValue(DisplayNameProperty, value);
DisplayText.Text = value;
}
}
// Using a DependencyProperty as the backing store for DisplayName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DisplayNameProperty =
DependencyProperty.Register("DisplayName", typeof(string), typeof(CustomControl), new PropertyMetadata("DisplayText"));
public CustomControl()
{
this.InitializeComponent();
}
}
Its structure is very simple:
<Grid>
<Button Name="ClickButton" Content="Button" Margin="171,165,0,0" VerticalAlignment="Top"/>
<TextBlock Name="DisplayText" HorizontalAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Center"/>
</Grid>
The problem is that when I click the button nothing happens and I am struggling to understand why.
DataObject shouldn't inherit from DependencyObject. It should be defined as a CLR object that implements INotifyPropertyChanged:
public class DataObject : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Also, the setter of the CLR wrapper for the dependency property in CustomControls should only set the value of the dependency property. You could set the value of the TextBlock using a PropertyChangedCallback:
public sealed partial class CustomControl : UserControl
{
public string DisplayName
{
get { return (string)GetValue(DisplayNameProperty); }
set { SetValue(DisplayNameProperty, value); }
}
public static readonly DependencyProperty DisplayNameProperty =
DependencyProperty.Register(nameof(DisplayName), typeof(string), typeof(CustomControl),
new PropertyMetadata("DisplayText", new PropertyChangedCallback(OnChanged)));
private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CustomControl customControl = (CustomControl)d;
d.DisplayText = e.NewValue as string;
}
public CustomControl()
{
this.InitializeComponent();
}
}

How to bind a WPF control to a property of an object which is in a dictionary?

I am new to WPF and learning how to bind the controls. I have created a UI which has different controls such as combobox and spinner (user defined control). Now I want to bind those controls such as combobox to a property of an object. Below is the sample code.
public class Parameter : INotifyPropertyChanged
{
protected decimal m_Code;
public decimal CODE
{
get { return m_Code; }
set
{
if (m_Code != value)
{
m_Code = value;
NotifyPropertyChanged("CODE");
}
}
}
protected decimal m_CurrentValue;
public decimal CURRENT_VALUE
{
get { return m_CurrentValue; }
set
{
if (m_CurrentValue != value)
{
m_CurrentValue = value;
NotifyPropertyChanged("CURRENT_VALUE");
}
}
}
protected Dictionary<int, string> m_ItemsDict;
public Dictionary<int, string> ItemsDict
{
get { return m_ItemsDict; }
set
{
//if (m_dict != value) Not comparing dictionaries. We should set if we get this request
{
m_ItemsDict = value;
NotifyPropertyChanged("ItemsDict");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
Below class is a singleton which has a dictionary where Key is an int and value is an object of parameter class.
public class ParameterDB: INotifyPropertyChanged
{
private static ParameterDB m_instance = new ParameterDB();
public static ParameterDB Instance
{
get { return m_instance; }
}
private Dictionary<int, Parameter> m_ParamDict;
private ParameterDB()
{
m_ParamDict = new Dictionary<int, Parameter>();
for(int i=0; i< 300; i++)
{
m_ParamDict.Add(i, new Parameter());
}
}
public Dictionary<int, Parameter> ParamDict
{
get
{
return m_ParamDict;
}
set
{
m_ParamDict = value;
NotifyPropertyChanged("ParamDict");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
Now I want to bind a control to, let's say, ParamDict[0].CURRENT_VALUE. I have created a custom combo box class.
public class MyComboBox : System.Windows.Controls.ComboBox
{
public readonly static DependencyProperty CodeProperty = DependencyProperty.Register(
"Code",
typeof(UInt32),
typeof(FlexSIMComboBox),
new PropertyMetadata(new UInt32()));
public UInt32 Code
{
get { return (UInt32)GetValue(CodeProperty); }
set
{
SetValue(CodeProperty, value);
}
}
public readonly static DependencyProperty ValueProperty = DependencyProperty.Register(
"Value",
typeof(decimal),
typeof(MyComboBox),
new PropertyMetadata(new decimal(0), ValueChangedCallback));
public decimal Value
{
get { return (decimal)GetValue(ValueProperty); }
set
{
SetValue(ValueProperty, value);
}
}
private static void ValueChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyComboBox myComboBox = d as MyComboBox;
if (myComboBox != null && e.NewValue is decimal)
{
// Check if new value is under the limit
decimal newValue = (decimal)e.NewValue;
myComboBox.SelectedValue = (int)newValue;
}
}
}
XAML file:
<local:MyComboBox
Grid.Column="1" x:Name="myCBCtrl" Margin="0,0,0,7" Width="180"
HorizontalAlignment="Left" VerticalAlignment="Bottom" IsReadOnly="true"
Code="{Binding CODE, Mode =TwoWay}"
Value="{Binding CURRENT_VALUE, Mode =TwoWay}"
ItemsSource="{Binding ItemsDict, Mode =TwoWay, NotifyOnTargetUpdated=True}"
SelectedValuePath="Key" DisplayMemberPath="Value" TargetUpdated="ComboBox_TargetUpdated"
StaysOpenOnEdit="True" SelectedIndex="0" SelectionChanged="ComboBox_SelectionChanged"/>
My question is how do I set DataContext for this control to an object which is in a dictionary?
For example to ParamDict[CODE]. CODE is also the key for the dictionary.
My intention is to build a dictionary and then bind my control to an item's property in that dictionary. So if the item's property is changed, it should reflect on the UI too. Any idea how to do that in XAML file.
I figured out how to do it.
<local:MyComboBox DataContext="{Binding Source={x:Static local:ParameterDB.Instance}}"
Grid.Column="1" x:Name="myCBCtrl" Margin="0,0,0,7" Width="180"
HorizontalAlignment="Left" VerticalAlignment="Bottom" IsReadOnly="true"
Code="{Binding Path=ParamDict[0].CODE, Mode =TwoWay}"
Value="{Binding Path=ParamDict[0].CURRENT_VALUE, Mode =TwoWay}"
ItemsSource="{Binding Path=ParamDict[0].ItemsDict, Mode =TwoWay, NotifyOnTargetUpdated=True}"
SelectedValuePath="Key" DisplayMemberPath="Value" TargetUpdated="ComboBox_TargetUpdated"
StaysOpenOnEdit="True" SelectedIndex="0" SelectionChanged="ComboBox_SelectionChanged"/>

Custom Control dependency property changed not firing when updating datacontext

I have a sample program to recreate the issue I am having.
Control:
public class PropertyUpdateControl : Control
{
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
nameof(Value), typeof(double), typeof(PropertyUpdateControl),
new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.None, ValueProperty_Changed));
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
private static void ValueProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var lPropertyUpdateControl = (PropertyUpdateControl) d;
lPropertyUpdateControl.Value_Changed((double)e.OldValue, (double)e.NewValue);
}
private void Value_Changed(double oldValue, double newValue)
{
this.Value = newValue;
this.Label = newValue.ToString();
}
public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
nameof(Label), typeof(string), typeof(PropertyUpdateControl), new PropertyMetadata("Label"));
public string Label
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
}
Main Window
public class MainWindowViewModel : ViewModelBase
{
private PropertyUpdateViewModel mPropertyUpdateViewModel;
public MainWindowViewModel()
{
this.PropertyUpdateViewModel = new PropertyUpdateViewModel();
this.ChangeCommand = new RelayCommand(this.ChangeImplementation);
}
public ICommand ChangeCommand { get; set; }
public void ChangeImplementation()
{
if (this.PropertyUpdateViewModel == null)
{
this.PropertyUpdateViewModel = new PropertyUpdateViewModel();
}
else
{
this.PropertyUpdateViewModel = null;
}
}
public PropertyUpdateViewModel PropertyUpdateViewModel
{
get { return this.mPropertyUpdateViewModel;}
set { this.Set(ref this.mPropertyUpdateViewModel, value); }
}
}
public class PropertyUpdateViewModel : ViewModelBase
{
private double mValue;
private static int sInstanceCounter = 0;
public PropertyUpdateViewModel()
{
Interlocked.Increment(ref sInstanceCounter);
this.Value = sInstanceCounter;
}
public double Value
{
get { return this.mValue; }
set { this.Set(ref this.mValue, value); }
}
}
XAML
<Window.Resources>
<local:MainWindowViewModel x:Key="mKeyMainWindowViewModel" />
<Style TargetType="{x:Type local:PropertyUpdateControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:PropertyUpdateControl}">
<TextBlock
Margin="25" Text="{TemplateBinding Label}"></TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="mDataTemplate" DataType="{x:Type local:PropertyUpdateViewModel}">
<local:PropertyUpdateControl
Value="{Binding Value}"></local:PropertyUpdateControl>
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<StaticResource ResourceKey="mKeyMainWindowViewModel"/>
</Window.DataContext>
<Grid>
<StackPanel>
<ContentPresenter Content="{Binding PropertyUpdateViewModel}" ContentTemplate="{StaticResource mDataTemplate}"/>
<Button Content="Update Value" Command="{Binding ChangeCommand}"></Button>
</StackPanel>
</Grid>
So basically the control seems to only be updated once. The label will always read 1 even after setting the view model to null and then to a new instance of the PropertyUpdateViewModel which will have an incrementing value. Why is the PropertyUpdateControl Value_Changed only called once and never called again even after the datacontext changes?
I found the issue to be in
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
nameof(Value), typeof(double), typeof(PropertyUpdateControl),
new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.None, ValueProperty_Changed));
It needs to be FrameworkPropertyMetadataOptions.BindsTwoWayByDefault.
This post has more info on binding for anyone interested https://www.tutorialspoint.com/wpf/wpf_data_binding.htm

Labeled ComboBox in Windows 10 Universal App

similar to my Labeled TextBox, which issues are resolved in:
Labeled TextBox in Windows Universal App
I got two issues in my Labeled Combobox, but first the Code:
Generic.xaml:
<Style TargetType="template:LabeledComboBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="template:LabeledComboBox">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="{TemplateBinding Label}" FontWeight="Bold" VerticalAlignment="Center" Margin="10,0" />
<ComboBox x:Name="PART_ComboBox" ItemsSource="{TemplateBinding ItemsSource}" SelectedIndex="{TemplateBinding SelectedIndex}" SelectedValue="{TemplateBinding SelectedValue}" SelectedValuePath="{TemplateBinding SelectedValuePath}" DisplayMemberPath="{TemplateBinding DisplayMemberPath}" VerticalAlignment="Center" Margin="20,0,10,0" Grid.Row="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
LabeledComboBox.cs:
[TemplatePart(Name = "PART_ComboBox", Type = typeof(ComboBox))]
public sealed class LabeledComboBox : Control, IParameterReturnable
{
public static readonly DependencyProperty LabelProperty = DependencyProperty.Register("Label", typeof(string), typeof(LabeledComboBox), new PropertyMetadata(""));
public string Label
{
get { return GetValue(LabelProperty).ToString(); }
set { SetValue(LabelProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(object), typeof(LabeledComboBox), new PropertyMetadata(null));
public object ItemsSource
{
get { return GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty SelectedIndexProperty = DependencyProperty.Register("SelectedIndex", typeof(int), typeof(LabeledComboBox), new PropertyMetadata(default(int)));
public int SelectedIndex
{
get { return (int) GetValue(SelectedIndexProperty); }
set { SetValue(SelectedIndexProperty, value); }
}
public static readonly DependencyProperty SelectedValueProperty = DependencyProperty.Register("SelectedValue", typeof(object), typeof(LabeledComboBox), new PropertyMetadata(null));
public object SelectedValue
{
get { return GetValue(SelectedValueProperty); }
set { SetValue(SelectedValueProperty, value); }
}
public static readonly DependencyProperty SelectedValuePathProperty = DependencyProperty.Register("SelectedValuePath", typeof(string), typeof(LabeledComboBox), new PropertyMetadata(default(string)));
public string SelectedValuePath
{
get { return GetValue(SelectedValuePathProperty).ToString(); }
set { SetValue(SelectedValuePathProperty, value); }
}
public static readonly DependencyProperty DisplayMemberPathProperty = DependencyProperty.Register("DisplayMemberPath", typeof(string), typeof(LabeledComboBox), new PropertyMetadata(default(string)));
public string DisplayMemberPath
{
get { return GetValue(DisplayMemberPathProperty).ToString(); }
set { SetValue(DisplayMemberPathProperty, value); }
}
private ComboBox _comboBox;
public LabeledComboBox()
{
this.DefaultStyleKey = typeof(LabeledComboBox);
}
public LabeledComboBox(List<Parameter> parameterList)
{
this.Label = parameterList[0].DisplayName ?? "";
this.ItemsSource = parameterList;
this.SelectedValuePath = "DefaultValue";
this.DisplayMemberPath = "DefaultValue";
this.SelectedIndex = 0;
this.DefaultStyleKey = typeof(LabeledComboBox);
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
_comboBox = GetTemplateChild("PART_ComboBox") as ComboBox;
if (_comboBox != null)
{
_comboBox.SelectionChanged += OnComboBoxSelectionChanged;
if (_comboBox.Items != null)
{
this.SelectedIndex = 0;
_comboBox.SelectedValue = _comboBox.Items[this.SelectedIndex];
}
}
}
private void OnComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.SelectedValue = _comboBox.SelectedValue;
}
public string GetKey()
{
return Label;
}
public string GetValue()
{
return SelectedValue.ToString();
}
}
It will be called in two different ways:
Dynamically in C#:
stackPanel.Add(new LabeledComboBox(parameterList));
Static in Xaml:
<templates:LabeledComboBox Label="Kategorien:" ItemsSource="{Binding ElementName=pageRoot, Path=FeedCategories}" DisplayMemberPath="Name" SelectedValuePath="Name" />
As I said before I got two issues with it:
How can I bind the SelectionChangedEvent to access it in Xaml || C#
As you can see, I try to preselect the first Item, which does not work and I don't know how to do it right
Thank you very much for all helpful and well meant answers in advance!
Instead of creating a custom control and recreating all needed dependency properties, I would suggest you use the Header and HeaderTemplate properties of the built in ComboBox, which will be displayed, just like in your LabeledComboBox, above the selection menu. Additionally the SelectionChanged event will be available.
So the usage in XAML would look like the following:
<ComboBox
DisplayMemberPath="Name"
Header="Kategorien:"
ItemsSource="{Binding ElementName=pageRoot, Path=FeedCategories}"
SelectedValuePath="Name"
SelectionChanged="OnSelectionChanged">
<ComboBox.HeaderTemplate>
<DataTemplate>
<TextBlock
Margin="10,0"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding}" />
</DataTemplate>
</ComboBox.HeaderTemplate>
</ComboBox>
But if you don't want to use the above method, to expose the selection changed event in your LabeledComboBox, add the following code:
private void OnComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.SelectedValue = _comboBox.SelectedValue;
this.RaiseSelectionChanged(e);
}
public event EventHandler<SelectionChangedEventArgs> SelectionChanged;
private void RaiseSelectionChanged(SelectionChangedEventArgs args)
{
if (SelectionChanged != null)
{
SelectionChanged(this, args);
}
}
Then you can use the created SelectionChanged event from XAML.

Inherited Value in Column

we have a wpf-window with some textboxes and a datagrid.
the textboxes descripe a parent (class a) object and the datagrid lists a collection of "childs" (class b => not derived from class a).
the childs can inherit values from the parent.
for example if the parent (class a) has a property Foo then the child object (class b) has a property Nullable which can either override the value of the parent or inherit the value of the parent.
now the datagrid should display the value in gray (if it is inherited) or in black (if the user overrides the value in the grid cell).
Unfortunatly Binding to InheritedText doesnt work. Does someone have any idea?
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<UserControls:InheritedTextBoxControl
Text="{Binding Path=?}"
InheritedText="{Binding Path=?}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
Thanks in advance
Tobi
--UPDATE--
xaml of InheritedTextBoxControl:
<UserControl x:Class="Com.QueoMedia.CO2Simulationstool.WPF.Utils.UserControls.InheritedTextBoxControl"
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"
mc:Ignorable="d"
Width="Auto"
Height="Auto"
Name="cnt">
<Grid x:Name="LayoutRoot"
Background="White">
<TextBox TextChanged="TextBoxTextChanged"></TextBox>
<TextBlock Name="inheritedText"
IsHitTestVisible="False"
Margin="4,0"
VerticalAlignment="Center"
Opacity="0.5"
FontStyle="Italic"></TextBlock>
</Grid>
CodeBehind:
public partial class InheritedTextBoxControl : UserControl {
private bool _isInherited;
public static readonly DependencyProperty InheritedTextProperty = DependencyProperty.Register("InheritedText", typeof(String), typeof(InheritedTextBoxControl), new PropertyMetadata(""));
public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(InheritedTextBoxControl), new PropertyMetadata(default(string)));
public InheritedTextBoxControl() {
InitializeComponent();
}
public string InheritedText {
get { return (string)GetValue(InheritedTextProperty); }
set {
SetValue(InheritedTextProperty, value);
inheritedText.Text = value;
}
}
private bool IsInherited {
get { return _isInherited; }
set {
_isInherited = value;
if (value) {
inheritedText.Opacity = 0.5;
} else {
inheritedText.Opacity = 0;
}
}
}
public string Text {
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
private void TextBoxTextChanged(object sender, TextChangedEventArgs e) {
if (((TextBox)sender).Text.Length > 0) {
IsInherited = false;
} else {
IsInherited = true;
}
Text = ((TextBox)sender).Text;
}
}
The problem is the setter of your InheritedText property. WPF won't call this setter when the property is set from XAML. See Checklist for Defining a Dependency Property, section Implementing the "Wrapper" for details.
You will have to update inheritedText.Text in a PropertyChangedCallback like this:
public static readonly DependencyProperty InheritedTextProperty =
DependencyProperty.Register(
"InheritedText", typeof(string), typeof(InheritedTextBoxControl),
new PropertyMetadata(string.Empty, InheritedTextChanged));
private static void InheritedTextChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
((InheritedTextBoxControl)d).inheritedText.Text = (string)e.NewValue;
}
public string InheritedText
{
get { return (string)GetValue(InheritedTextProperty); }
set { SetValue(InheritedTextProperty, value); } // only call SetValue here
}
If someone is interested in the solution:
we did it using a CellTemplate containing a CustomControl name MaskedTextbox that has three properties (MaskedText, Text, IsMaskTextVisible) and a CellEditingTemplate to override the data.
The values are bound to an InheritableValueViewModel.
Tobi

Categories

Resources