C#/WPF: Dependency Property is not updating the bound Property? - c#

I'm trying to bind a Dependency Property from my UserControl to my MainViewModel.
This is how the DependencyProperty looks like:
public static DependencyProperty ItemHasChangesProperty = DependencyProperty.Register("ItemHasChanges",
typeof(bool),
typeof(MyUserControl),
new PropertyMetadata(null));
public bool ItemHasChanges
{
get { return (bool)GetValue(ItemHasChangesProperty); }
set { SetValue(ItemHasChangesProperty, value); }
}
My XAML:
<local:MyUserControl ItemHasChanges="{Binding Path=Changes}" Grid.Row="4" />
Now when debugging and checking the Set-Accessor of bool Changes, I see that it never gets accessed when I set in the UserControl ItemHasChanges = true;
Any idea what I'm doing wrong here?
Thanks!
Cheers

Got it.. I had to change
<local:MyUserControl ItemHasChanges="{Binding Path=Changes}" Grid.Row="4" />
to
<local:MyUserControl ItemHasChanges="{Binding Path=Changes, Mode=OneWayToSource}" Grid.Row="4" />
Took me about 3h to figure it out.. haha :-)
Cheers

Are you setting ItemHasChanges on the control directly (as in, not by updating the binding source)? If so, that will remove the binding.

Related

C# WPF: Dependency Property vs MVVM

I need to create a component with Dependency Property, I don´t know how to bind the data from another user control, can someone explain it to me? I don´t think i need to create a viewmodel for the component.
Here´s my code:
Here I call the component
<DockPanel Grid.Row="1">
<ctrls:CustomComboBox Collection="{Binding Path=TestingCollection}" SelectedCollectionItem="{Binding Path=SelectedItem}"
CreateCommand="" DeleteCommand="" UpdateCommand=""/>
</DockPanel>
And Here it´s my component
<ComboBox Grid.Column="0" SelectedValue="{Binding SelectedCollectionItem,ElementName=CustomComboBoxControl, Mode=TwoWay}"
ItemsSource="{Binding Collection}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
/>
Here it is my xaml.cs
public static readonly DependencyProperty CollectionProperty =
DependencyProperty.Register("Collection", typeof(ObservableCollection<object>), typeof(CustomComboBox));
public ObservableCollection<object> Collection
{
get { return (ObservableCollection<object>)GetValue(CollectionProperty); }
set { SetValue(CollectionProperty, value); }
}
public static readonly DependencyProperty SelectedCollectionItemProperty =
DependencyProperty.Register("SelectedCollectionItem", typeof(object), typeof(CustomComboBox));
public object SelectedCollectionItem
{
get { return (object)GetValue(SelectedCollectionItemProperty); }
set { SetValue(SelectedCollectionItemProperty, value); }
}
EDIT:
I tryed creating a VM and setting the data context, and works, but dependency properties seem pointless
EDIT 2:
I succeded, i delete my VM i was lacking and ElementName in ItemSource
I succeded, i delete my VM i was lacking and ElementName in ItemSource

How to resolve Binding can only be set on a DependencyProperty

Got a user control defined as follows:
public partial class EveBlueprintsGrid : UserControl
{
public static DependencyProperty BlueprintListProperty = DependencyProperty.Register(
"EveBlueprints",
typeof(EveBlueprintList),
typeof(EveBlueprintsGrid));
public EveBlueprintList EveBlueprints
{
get { return (EveBlueprintList)GetValue(BlueprintListProperty); }
set { SetValue(BlueprintListProperty, value); }
}
with XAML:
<DataGrid x:Name="BlueprintsDataGrid" Grid.Row="0" IsReadOnly="True" ItemsSource="{Binding Path=EveBlueprints.Blueprints, ElementName=uc}" AutoGenerateColumns="False">
With the calling XAML:
<Blueprints:EveBlueprintsGrid Grid.Column="1" EveBlueprints="{Binding Blueprints}"/>
I am getting the "A Binding can only be set on a DependencyProperty of a DependencyObject." When I look at the calling XAML.
EveBlueprints contains property Blueprints which is an ObservableCollection.
What's interesting is I have an almost exact copy of this on another object which isn't giving me the errors I'm seeing.
What am I missing here?

Custom Control DataContext Doesn't Work

I have a Custom Controller that works fine like this:
<Controller:DLBox ID="{Binding SHVM.Selected.ID}" />
I mean binding ID to a property in ViewModel. But when I want to bind it like this:
<ScrollViewer DataContext="{Binding SHVM.Selected}">
<Controller:DLBox ID="{Binding ID}" />
</ScrollViewer>
Binding to DataContext of parent, It doesn't work at all. I have some other Custom Controllers and they do fine, But I don't know what the hell is this one's problem!
This is the controller:
public partial class DLBox : UserControl
{
public static DependencyProperty IDProperty =
DependencyProperty.Register("ID", typeof(int), typeof(DLBox),
new FrameworkPropertyMetadata(0,FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(o, e) => (o as DLBox).IDPropertyChanged((int)e.NewValue)));
public int ID { get; set; }
private void IDPropertyChanged(int e)
{
ID = e;
}
}
Could someone please tell my what's wrong? Because I'm debugging for 6 hours straight and didn't find anything! Thanks a lot.
‌
‌
UPDATE:
That worked with just adding:
<... DataContext={Binding} .../>
And I don't know why!
Now the real problem is that I want to use this inside 2 ItemsControls and even with DataContext Still doesn't work.
(Just for clarification, I have 2 Lists inside each other. Think about the first one like 10 schools First List, and inside each school there is some studens Second List)
<ItemsControl ItemsSource="{Binding Source={StaticResource Locator}, Path=SHVM.Extra.Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl DataContext="{Binding}" ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Controller:DLBox DataContext="{Binding}" ID="{Binding ID}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
<ItemsControl.ItemTemplate>
</ItemsControl>
UPDATE 2:
ID is just a TextBlock In a UserControl. There is nothing that I can show here!
All I did, Just set the Text of TextBlock inside the PropertyCallBack (Didn't use MVVM inside my controllers):
<TextBlock x:Name="txtIDValue"/>
And inside CodeBehind:
private void IDPropertyChanged(string e)
{
ID= e;
txtIDValue.Text = e;
}
There is nothing relevant to this problem, And that's why I couldn't figure it out!
Appreciate any help.
ANSWER:
After 12 hours working on it, I find out that It was an idiotic mistake! I don't know why and when I set DataContext in my Controller's XAML!
Anyway, Thanks.
Your dependency property declaration is wrong, because the getter and setter of the CLR wrapper must call the GetValue and SetValue methods respectively. Besides that, your PropertyChangedCallback is redundant. There is no need to set the property again in a callback that is called when the property value has just been set.
The declaration should look like this:
public static readonly DependencyProperty IDProperty = DependencyProperty.Register(
"ID", typeof(int), typeof(DLBox),
new FrameworkPropertyMetadata(
0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public int ID
{
get { return (int)GetValue(IDProperty); }
set { SetValue(IDProperty, value); }
}

XAML binding not working on dependency property?

I am trying (and failing) to do data binding on a dependency property in xaml. It works just fine when I use code behind, but not in xaml.
The user control is simply a TextBlock that bind to the dependency property:
<UserControl x:Class="WpfTest.MyControl" [...]>
<TextBlock Text="{Binding Test}" />
</UserControl>
And the dependency property is a simple string:
public static readonly DependencyProperty TestProperty
= DependencyProperty.Register("Test", typeof(string), typeof(MyControl), new PropertyMetadata("DEFAULT"));
public string Test
{
get { return (string)GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
I have a regular property with the usual implementation of INotifyPropertyChanged in the main window.
private string _myText = "default";
public string MyText
{
get { return _myText; }
set { _myText = value; NotifyPropertyChanged(); }
}
So far so good. If I bind this property to a TextBlock on the main window everything works just fine. The text update properly if the MyText changes and all is well in the world.
<TextBlock Text="{Binding MyText}" />
However, if I do the same thing on my user control, nothing happens.
<local:MyControl x:Name="TheControl" Test="{Binding MyText}" />
And now the fun part is that if I do the very same binding in code behind it works!
TheControl.SetBinding(MyControl.TestProperty, new Binding
{
Source = DataContext,
Path = new PropertyPath("MyText"),
Mode = BindingMode.TwoWay
});
Why is it not working in xaml?
The dependency property declaration must look like this:
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register(
nameof(Test),
typeof(string),
typeof(MyControl),
new PropertyMetadata("DEFAULT"));
public string Test
{
get { return (string)GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
The binding in the UserControl's XAML must set the control instance as the source object, e.g. by setting the Bindings's RelativeSource property:
<UserControl x:Class="WpfTest.MyControl" ...>
<TextBlock Text="{Binding Test,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</UserControl>
Also very important, never set the DataContext of a UserControl in its constructor. I'm sure there is something like
DataContext = this;
Remove it, as it effectively prevents inheriting a DataContext from the UserConrol's parent.
By setting Source = DataContext in the Binding in code behind you are explicitly setting a binding source, while in
<local:MyControl Test="{Binding MyText}" />
the binding source implicitly is the current DataContext. However, that DataContext has been set by the assignment in the UserControl's constructor to the UserControl itself, and is not the inherited DataContext (i.e. the view model instance) from the window.

wpf target property dependency

I'm trying to create a simple Bindable property called MyBoolValue in my UserControl class
First, here the xaml
<UserControl x:Class="TMDE.Controls.SimNaoRadioPicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="16"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<CheckBox Grid.Column="0" Content="Teste" IsChecked="{Binding Path=MyBoolValue}" x:Name="chk" />
</Grid>
</UserControl>
And here the code-behind:
public partial class SimNaoRadioPicker : UserControl
{
public SimNaoRadioPicker()
{
InitializeComponent();
}
public bool? MyBoolValue
{
get
{
return (bool?)GetValue(MyCustomPropertyProperty);
}
set
{
SetValue(MyCustomPropertyProperty, value);
}
}
// Using a DependencyProperty as the backing store for MyCustomProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyCustomPropertyProperty =
DependencyProperty.Register("MyBoolValue",
typeof(bool?), typeof(SimNaoRadioPicker),
new UIPropertyMetadata(MyPropertyChangedHandler));
public static void MyPropertyChangedHandler(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
// Get instance of current control from sender
// and property value from e.NewValue
// Set public property on TaregtCatalogControl, e.g.
((SimNaoRadioPicker)sender).chk.IsChecked = (bool?)e.NewValue;
}
}
Now, when a try to use this control in another Window, like this:
<my:SimNaoRadioPicker x:Name="test" MyBoolValue="{Binding QCV_Localizacao_Reutilizacao}" Height="16" HorizontalAlignment="Left" Margin="287,456,0,0" VerticalAlignment="Top" Width="167" />
the Binding doesnt working, the property QCV_Localizacao_Reutilizacao doesnt get update and vice-versa.
The DataContext of the Window its a class that implements INotifyPropertyChanged, so the
property "QCV_Localizacao_Reutilizacao" should work ok.
Also if I use a regular CheckBox instead of my UserControl, its works okay
What I'm doing wrong?
I would remove the nullable part of the boolean and just make it a boolean, then set binding modes to two way.
There are two major issues -
First, your binding mode needs to be TwoWay which you can achieve in two ways -
Either specifed it to be TwoWay in xaml like this -
<my:SimNaoRadioPicker MyBoolValue="{Binding QCV_Localizacao_Reutilizacao,
Mode=TwoWay}"/>
The drawback with above apporach is that you have to explicitly set the mode whenever you are using the UserControl's instance.
Another approach would be to modify your DP itself to say that it always be bind by default in a TwoWay mode like this using FrameworkPropertyMetadata -
public static readonly DependencyProperty MyCustomPropertyProperty =
DependencyProperty.Register("MyBoolValue",
typeof(bool?), typeof(SimNaoRadioPicker),
new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
MyPropertyChangedHandler));
Secondly, QCV_Localizacao_Reutilizacao property lies in your Window's DataContext. But, by default any control will look for binding in its own dataContext so you explicilty need to tell it to look into Window's DataContext using RelativeSource like this -
<my:SimNaoRadioPicker MyBoolValue="{Binding QCV_Localizacao_Reutilizacao,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=Window}/>

Categories