Cell Style Binding does not Update on Value Change - c#

I have a DataGrid which is bound to a Collection of Objects. Every DataGridColumn is created in code behind.
The Background of these columns is dependent on different values of the object. I create the background binding in the CellStyle (as it should not override default style's from triggers).
var backgroundBinding = new Binding
{
Converter = new MyBindingConverter(),
ConverterParameter = new MyConverterParameter()
};
cellStyle.Setters.Add(new Setter(Control.BackgroundProperty, backgroundBinding));
As you can see it binds directly to the element. As different value are changing the value of the Columns is updated accordingly, but the converter of the Binding is not called.
I tried calling OnPropertyChanged(null) to show that the object was updated, but sadly this does not work.

Did you try to specify Path for backgroundBinding? Something like:
var backgroundBinding = new Binding
{
Converter = new MyBindingConverter(),
ConverterParameter = new MyConverterParameter(),
ElementName = YourElementName,
Path = PropertyOnElement
};
cellStyle.Setters.Add(new Setter(Control.BackgroundProperty, backgroundBinding));

Related

PropertyChanged event fired but value hasn't changed

I have a custom control dynamically created by another control, I want to change its VisualState based on the VisualState of the parent.
VisualState is a DependencyProperty that accept an enumerator, the control internally uses it inside the OnPropertyChange event to change size and internal layout.
The property are made identical on both controls (of course except the type).
public ControlSize VisualState
{
get { return (ControlSize)GetValue(VisualStateProperty); }
set
{
if (value != VisualState)
{
SetValue(VisualStateProperty, value);
}
}
}
public static readonly DependencyProperty VisualStateProperty = DependencyProperty.RegisterAttached(nameof(VisualState), typeof(ControlSize), typeof(CountersListControl), new PropertyMetadata(ControlSize.Large, OnVisualStateChanged));
The parent control dynamically allocate the component and binds its VisualState to the new control VisualState:
CounterControl cc = new CounterControl();
cc.SetBinding(CounterControl.ValueProperty, new Binding() { Path = new PropertyPath(nameof(Counter.Amount)), Source = counter, Mode = BindingMode.TwoWay });
//cc.DataContext = this;//I tried with it, but it doesn't change a thing
cc.SetBinding(CounterControl.VisualStateProperty, new Binding() { Path = new PropertyPath(nameof(VisualState)), Source = this, Mode = BindingMode.OneWay });
The Value property binds without any issue to Counter.Amount, and looks that VisualState does too.
BUT the OnVisualState method is called when the parent is changed, while the children value is not.
UPDATE: I debugged the binding as suggested by #EdPlunkett, and I was getting the following message:
Error: Converter failed to convert value of type 'Windows.Foundation.Int32' to type 'ControlSize';
ControlSize is an enumerable, so it should be able to convert it.
This happens because somehow it can't convert an Int32 into an enumerable (even if the source is the same enumerable).
I solved creating an IValueConverter that converts Int32/ControlSize types and assigning it to the binding.
Binding visualStateBinding = new Binding() { Path = new PropertyPath(nameof(VisualState)), Source = this, Mode = BindingMode.OneWay, Converter = new ControlSizeConverter() };

How to create label and dynamically add binding to its content property in WPF

I'm trying to create a label at runtime and connect it's Content property to another TextBox control which is in my UserControl called MyLabelSettings.
This is what I got so far:
Label currCtrl = new Label();
MyLabelSettings currCtrlProperties = new MyLabelSettings();
// Bindings to properties
Binding binding = new Binding();
binding.Source = currCtrlProperties.textBox_Text.Text;
binding.Path = new PropertyPath(Label.VisibilityProperty);
BindingOperations.SetBinding(currCtrl.Content, Label.ContentProperty, binding);
The last row shows an error which I did not figure out how to solve:
The best overloaded method match for 'System.Windows.Data.BindingOperations. SetBinding(System.Windows.DependencyObject, System.Windows.DependencyProperty, System.Windows.Data.BindingBase)' has some invalid arguments
I have in MyLabelSettings the implementation of INotifyPropertyChanged
which has the following code when the TexBox.Text changes
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
InvokePropertyChanged(new PropertyChangedEventArgs("TextChanged"));
}
Is there a better way to bind these 2? Or am I doing something wrong in this one?
Thanks!
The problem is simpler than you realize:
This:
binding.Source = currCtrlProperties.textBox_Text.Text;
binding.Path = new PropertyPath(Label.VisibilityProperty);
BindingOperations.SetBinding(currCtrl.Content, Label.ContentProperty, binding);
Should be this:
//The source must be an object, NOT a property
binding.Source = currCtrlProperties;
//Since the binding source is not a DependencyObject, we using string to find it's property
binding.Path = new PropertyPath("TextToBind");
BindingOperations.SetBinding(currCtrl, Label.ContentProperty, binding);
Before, you were attempting to bind the value to an object's property via a property. Now, you're binding the value to an object's property via an object (:
Notes:
You are attempting to bind the text of a control that exists in an instance of a class you just made.
MyLabelSettings currCtrlProperties = new MyLabelSettings();
I base this assumption off this line:
currCtrlProperties.textBox_Text.Text;
Which appears to contain a text control of some sort. Rather, you want bind the text of a property that exists in an instance of a class you make, not a control.

show values of mutiple properties in tool tip wpf XamDataGrid

I have xamdatgrid which I add columns dynamically. I am adding a ToolTip to the column using following code.
Binding toolTipBinding = new Binding();
toolTipBinding.Path = new PropertyPath("DataItem.Property1");
Setter toolTipSetter = new Setter();
toolTipSetter.Property = ToolTipProperty;
toolTipSetter.Value = toolTipBinding;
cellValuePresenterStyle.Setters.Add(toolTipSetter);
Using this I was able to display the value of Property1 as the ToolTip. I have another Property in the class (property2). I want to display the values of both Properties in the ToolTip. How can I achieve that
I can't understand why are you not doing it in XAML code. But if you do want to achieve this in using code behind. Then do following steps
Use MultiBinding class to do a binding of tooltip.
create two bindings for two properties (Property1 & Property2) and add them in multibinding you created in step 1 using Add method.
create a converter (IMultiValueConverter) to write a logic to concatinate property1 & property2.
Assign the binding in the setter value as you have already done.
Binding b1=new Binding();
b1.Path = new PropertyPath("DataItem.Property1");
Binding b2=new Binding();
b2.Path = new PropertyPath("DataItem.Property2");
var toolTipBinding = new MultiBinding();
toolTipBinding.Bindings.Add(b1);
toolTipBinding.Bindings.Add(b2);
toolTipBinding.Converter = new ///your converter

Converter not working when DataTemplate is loaded in code behind

I loaded the datatemplate for image column in code behind . Please refer the below code snippet,
FrameworkElementFactory fef = new FrameworkElementFactory(typeof(Image));
Binding bind = new Binding() { Path=new PropertyPath(imagecolumn.MappingName),Converter = new StringToImageConverter(),Mode=BindingMode.TwoWay };
fef.SetBinding(Image.SourceProperty,new Binding(imagecolumn.MappingName));
DataTemplate template = new DataTemplate() { VisualTree = fef };
this.imagecolumn.CellItemTemplate = template;
But my converter is not invoked. I need to load different images in each row of the column . Am i missed anything ? Please share any idea
You instantiate a new Binding but you never use it. Do this:
FrameworkElementFactory fef = new FrameworkElementFactory(typeof(Image));
Binding bind = new Binding() { Path=new PropertyPath("MappingName"),Converter = new StringToImageConverter(),Mode=BindingMode.TwoWay,Source=imagecolumn };
fef.SetBinding(Image.SourceProperty, bind); // here you just created
//another instance of Binding instead of using your bind variable
DataTemplate template = new DataTemplate() { VisualTree = fef };
this.imagecolumn.CellItemTemplate = template;
EDIT:
Have a look at FrameworkElementFactory. In the remarks it sais:
This class is a deprecated way to programmatically create templates, which are subclasses of FrameworkTemplate such as ControlTemplate or DataTemplate; not all of the template functionality is available when you create a template using this class. The recommended way to programmatically create a template is to load XAML from a string or a memory stream using the Load method of the XamlReader class.
Maybe you should do it the recommended way.

Setting the Datacontext of a dynamically created control

I have a UserControl (Map) that contains a Canvas control. I am dynamically adding a control (Gate) to this canvas from the code behind.
I want the Gate objects DataContext to be the "Gate" property of the Map's DataContext. This is being done in the code behind.
Binding dataContextBinding = new Binding();
dataContextBinding.RelativeSource = new RelativeSource(RelativeSourceMode.Self);
dataContextBinding.Path = new PropertyPath("DataContext.SelectedLevelModule.Gate");
dataContextBinding.Mode = BindingMode.TwoWay;
dataContextBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(gate, DataContextProperty, dataContextBinding);
After this block of code runs, the gate.DateContext is null...
Any ways this can be done? Drawing a blank..
Thanks
Harold
You are setting the property path to DataContext.SelectedLevelModule.Gate. You are then assigning the binding to the DataContextProperty. I think what is happening is that the path is now gate.DataContext.DataContext.SelectedLevelModule.Gate.
Try removing DataContext from your PropertyPath and see if that fixes it. You are already assigning it to the DataContext, you should not have to specify it in the Path.
var dataContextBinding = new Binding();
dataContextBinding.Path = new PropertyPath("SelectedLevelModule.Gate");
BindingOperations.SetBinding(gate, DataContextProperty, dataContextBinding);

Categories