wpf control background 2-way binding only working one way - c#

I have this button that has a two way binding on the background brush, I've set up a dependency property, I'm also making use of the INotifyPropertyChanged interface. But I'm still having problems with two way bindings.
If I update the property that's bound to the button, the buttons background changes, like i would expect however if I update the buttons background directly ("button.Background = Brushes.Blue") the property doesn't get updated.
Here is the xaml for the button:
<Button Background="{Binding ElementName=MainWindow,Path=TitleBrush,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
The property:
public Brush TitleBrush
{
get
{
return (Brush)GetValue(TitleBrushProperty);
}
set
{
if (!_graph.TitleBrush.Equals(value))
{
_graph.TitleBrush = value;
SetValue(TitleBrushProperty, value);
NotifyPropertyChanged(nameof(TitleBrush));
}
}
}
public static readonly DependencyProperty TitleBrushProperty =
DependencyProperty.Register(nameof(TitleBrush), typeof(Brush), typeof(MainWindow));
The two ways I change the background color:
TitleBrush = Brushes.Red; // This works great
button.Background = Brushes.Red; // This changes the background but doesn't update the property
Any help is appreciated.

TwoWay is for setting values to a bound property and accepting a value back. In your case, you are setting it one way for both cases -- to the control. The control isn't sending a property value back. TwoWay is really meant for input fields. If you are using code-behind, you should use the bound property.
As the BindingMode Enumeration documentation states:
Causes changes to either the source property or the target property to automatically update the other. This type of binding is appropriate for editable forms or other fully-interactive UI scenarios.

Related

How to control two-way binding initial update direction

Every now and then I face a situation where I need to set a two-way binding on a property where it would be preferred that upon setting the binding the initial update will be performed in target-to-source direction and not source-to-target. That is, when the binding is set the source property value is updated so that it reflects the target property value, which stays unchanged.
Is such scenario possible? And if it is, how can it be accomplished?
Of course there are several workarounds, such as caching the target property value, setting the binding and then restoring the cached value, but I'm interested a direct answer rather than a workaround.
Example
Let's say we have a TextBox with Text property set to "foo". Also, we have a view-model with Name property (of type string) set to null. Now what I want to achieve is to bind the Text property to the Name property while preserving the "foo" value. Important thing here is to avoid setting Text to null and then back to "foo" (for whatever reason, e.g. because clearing the TextBox causes other controls to clear as well).
Then the best practice is 1) to read the existing value from the control (Clearly Xaml is not supposed to have data binding). 2) At "Load" event, the control needs to create and establish data binding by calling "SetBinding." 3) Finally get the binding expression for the control and update source with the value from 1).
All the code should be implemented in "View" code, not in "ViewModel."
e.g.) This code snippet is not tested, but has come from my head in the ball park.
private void Loaded(object sender, RoutedEventArg arg)
{
...
var text = textBox.Text;
var binding = new Binding();
... binding property here
textBox.SetBinding(TextBox.TextProperty, binding);
textBox.Text = text;
var expression = BindingExpression.GetBindingExpression(textBox, TextBox.TextProperty);
expression.UpdateSource();
}
How about creating an Attached Property that can be used to any UIElement? This should address the universality requirement.
<TextBlock Text="foo"
GlobalAttachedProperty:Value="{Binding Path=A_ValueFromVM_OR_SomeWhereElse}"
GlobalAttachedProperty:Property="Text"/>
The Value property just contain a callback to set the binding when there is a change. You can add some fancy routine that do this just once.
Then the Property is just a way to get the actual property for binding purposes. There are some other way to accomplish this but this is the more direct way.
I think that the best option is to set a default value in your model class. This is the purpose of the view model.

Automatically bind event to a Dependency Properties Value

I am trying to set up a custom user control derived from the ToggleButton control.
I would like to set up two new commands, CheckedCommand and UncheckedCommand.
I have defined the commands as below, and am able to bind to them. And I am firing them via some internal events. It all works great.
However I would like to be able to have these commands disable the button as the usual command interface does with CanExecute. As I understand it I need to use the CanExecuteChanged event and set the IsEnabled Property in here. However when am I supposed to bind this event, I looked at using the PropertyChangedCallback event of the DependancyProperty but then how do I unsubscribe the from any previously bound command.
It all ends up looking a bit convoluted, am I missing something simple or is this just the way it is.
[Description("Checked Command"), Category("Common Properties")]
public ICommand CheckedCommand
{
get { return (ICommand)this.GetValue(CheckedCommandProperty); }
set { this.SetValue(CheckedCommandProperty, value); }
}
public static readonly DependencyProperty CheckedCommandProperty = DependencyProperty.Register(
"CheckedCommand", typeof(ICommand), typeof(ToggleTextButton));
I may not understand your specific requirements but in general I wouldn't do it like this. WPF has been primarily designed to be data-driven, whereas here you're implementing event-driven notification. Events are the old-school WinForms way of doing things and they were added mainly to facilitate an easier transition from WinForms to the new data-bound architecture.
IMO a more "correct" solution here would be to expose a boolean dependency property i.e. basically the same thing as the "Checked" property in checkbox. Users of your control can then bind that one property to a boolean property in their view models whose setter will be called whenever the state changes. A second "Enabled" DP will allow both you and your user to control whether or not the control should be enabled, again similar to how it's implemented in Checkbox.
To ask an obvious question though if it's checkbox functionality you're after then why aren't you just templating checkbox? Do you really need a usercontrol for what it is you're trying to achieve?

Setting a dependency property of a control in the custom control

I have a infragistics grid that i have inherited from in the my solution . Now i would like to set a dependency property defined in the infragistics grid control in my custom control code behind and not in xaml . How do i accomplish setting the dependency property ?
I was able to set the backing CLR property in my custom control but as expected it didnt trigger off the gridcontrol UI change as expected and hence i m forced to set the dependency property in my custom control .
How exactly do i accomplish this ?
Normally a DependencyProperty is declared like this with CLR property 'wrappers':
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register(
"Items", typeof(ObservableCollection<string>), typeof(YourControlType));
public ObservableCollection<string> Items
{
get { return (ObservableCollection<string>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
If the Grid that you inherited declared their DependencyProperties like this, then you should be able to use the CLR wrapped properties directly like this:
Items = new ObservableCollection<string>();
Items.Add("Something");
If they didn't do that, then you can see in the wrapped properties how to access them:
anInstanceOfYourControlType.SetValue(ItemsProperty, "some value");
There is of course a chance that they declared internal properties in which case you won't be able to access them, but you should be able to find that out from their online documentation.
You can use DependencyProperty SetValue to set local value of Dependency Property.
dataGridObject.SetValue(DataGrid.ItemsSourceProperty, value);
But in case your DP binded with some CLR property and you want to update it as well, use SetCurrentValue method.
dataGridObject.SetCurrentValue(DataGrid.ItemsSourceProperty, value);

DependencyProperty default value and subclassed DataGrid breaks properties

I've come across some strange behaviour while trying to subclass wpf's DataGrid control.
Let's say I have:
class CustomDataGrid<T> : DataGrid { ... }
class FooDataGrid : CustomDataGrid<Foo> { }
And some xaml:
<local:FooDataGrid ItemsSource="..." SelectionMode="Single" SelectionUnit="FullRow" />
Everything works fine, and I can only select one row at a time. If I however attempt to change the defaults for SelectionMode/SelectionUnit by doing this:
static CustomDataGrid()
{
DataGrid.SelectionModeProperty.OverrideMetadata( typeof( CustomDataGrid<T> ), new FrameworkPropertyMetadata( DataGridSelectionMode.Single ) );
DataGrid.SelectionUnitProperty.OverrideMetadata( typeof( CustomDataGrid<T> ), new FrameworkPropertyMetadata( DataGridSelectionUnit.FullRow ) );
}
And change the xaml to:
<local:FooDataGrid ItemsSource="..." />
It does not seem to care about my defaults, and I can select multiple rows. Now, the weird thing is that if I try to set the properties manually in xaml again (while still having the defaults in the static constructor), I can still select multiple rows. So somehow overriding the metadata screws with the workings of those dependency properties, causing wpf to not care about the values set in the xaml.
Does anyone have a clue what is going on here?
Actual multiselect behavior is controlled by CanSelectMultipleItems property, which defaults to true and is only updated when SelectionMode property changes. Overriding default value won't call property changed handler, so CanSelectMultipleItems remains true. Now if you try to set values in XAML, dependency property system starts working against you: default value is DataGridSelectionMode.Single, and you are setting property to the same value, so property changed handler is not called again and nothing happens.
Simpliest solution - add a non-static constructor and initialize CanSelectMultipleItems property:
public CustomDataGrid()
{
CanSelectMultipleItems = SelectionMode != DataGridSelectionMode.Single;
}
Also you could declare custom style for your datagrid and set property values in style - it seems like a more "WPF-way" to do such things.

Any way to get event, when some WPF data binding update occurs?

Any way to get event, when some WPF data binding update occurs?
UPD I'm developing a custom control extending one of standard controls. I need to get notification, when DataContext property of the ancestor control changes, or data binding causes update of any property of the ancestor control.
It sounds like you require: INotifyPropertyChanged to be implemented on your View Model. This obviously depends on your implementation but this is assuming you've followed MVVM.
This then allows you to carry out some work based on the value of a bound property changing (and an event being raised).
As others already mentioned, if your object
implements INotifyPropertyChanged and
the property supports it, register to PropertyChanged and you will be informed about changes.
If you are in a DependencyObject and add your own DependencyProperty, register a DependencyPropertyChangedEventHandler in the metadata to be informed when the property changes-
If you have a DependencyObject and
the property is a DependencyProperty, you can override
OnPropertyChanged. It will be called
every time, a DependencyProperty
value has been changed. You can then filter out the property-changes you are interested in.
If you are outside of a
DependencyObject and want to
listen to a changed value of a DepenendencyProperty, you can use
the DependencyPropertyDescriptor to register for value changes. However take care, using this may produce memory-leaks.
If you're talking about getting a notification from a control perspective (i.e. when a dependency property has been bound to) you can provide a method that will be called passing the value:
public DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
"ItemsSource",
typeof(IList),
typeof(CustomGridControl),
new FrameworkPropertyMetadata(null, OnItemsSourceChanged));
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
e.NewValue;
}

Categories