MVVM DependencyProperty doesn't update when RaisePropertyChanged - wpf

I have usercontrol, and there is a DependencyProperty defined in it.
#region ImageUri
public static readonly DependencyProperty ImageUriProperty = DependencyProperty.Register(
"ImageUri",
typeof(string),
typeof(ScrollableCanvas),
new PropertyMetadata(new PropertyChangedCallback(ImageUriPropertyChangedCallback)));
private static void ImageUriPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
ScrollableCanvas main = sender as ScrollableCanvas;
if (main != null)
{
main.ImageUri = (string)e.NewValue;
}
}
public string ImageUri
{
get
{
return (string)GetValue(ImageUriProperty);
}
set
{
SetValue(ImageUriProperty, value);
UpdateImage();
}
}
#endregion
In the Xaml, I bind a value to it like this
<my:ScrollableCanvas Name="scrollableCanvas1" ImageUri="{Binding Path=LayerImage}" />
when I update the LayerImage in the viewmodel, the ImageUri property does not update.
Can some help on this? Thanks.
BTW: The value is updated when I set the LayerImage in the constructor of the viewmodel.

You shouldn't include your UpdateImage call in your setter, but rather in the property changed callback.
public static readonly DependencyProperty ImageUriProperty = DependencyProperty.Register(
"ImageUri",
typeof(string),
typeof(ScrollableCanvas),
new PropertyMetadata(new PropertyChangedCallback(ImageUriPropertyChangedCallback)));
private static void ImageUriPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
ScrollableCanvas main = sender as ScrollableCanvas;
if (main != null)
{
// Since ImageUri has already been called at this point, you can just update your image here...
main.UpdateImage();
}
}
public string ImageUri
{
get
{
return (string)GetValue(ImageUriProperty);
}
set
{
SetValue(ImageUriProperty, value);
}
}

Related

WPF: Passing main form property to user control

I have set up my own user control which contains two ComboBoxes, each ComboBox item source is bound to a DependencyProperty. The issue I am having is passing a property from the form that uses my user control to the user control.
Below is my user control:
public static readonly DependencyProperty ListOneProperty = DependencyProperty.Register
("ListOne", typeof(List<int>), typeof(LinkedComboBox), new PropertyMetadata(new List<int>()));
public List<int> ListOne
{
get { return (List<int>)GetValue(ListOneProperty); }
set { SetValue(ListOneProperty, value); }
}
public static readonly DependencyProperty ListTwoProperty = DependencyProperty.Register
("ListTwo", typeof(List<string>), typeof(LinkedComboBox), new PropertyMetadata(new List<string>()));
public List<string> ListTwo
{
get { return (List<string>)GetValue(ListTwoProperty); }
set { SetValue(ListTwoProperty, value); }
}
public LinkedComboBox()
{
InitializeComponent();
FillListOne();
}
And below is my MainWindow xaml:
<control:LinkedComboBox x:Name="LinkedBox" ListTwo="{Binding MyList}"/>
And MainWindow.xaml.cs:
static List<string> _myList = new List<string>{"abc","efg"};
public List<string> MyList
{
get { return _myList; }
set { _myList = value; }
}
public MainWindow()
{
InitializeComponent();
}
What do I need to get the User Control to accept a binding from the Main Window?
Everything is fine except that you need a PropertyChangedCallback to handle your property.
Here is a simple example
public static readonly DependencyProperty ListTwoProperty = DependencyProperty.Register
("ListTwo", typeof(List<string>), typeof(LinkedComboBox), new PropertyMetadata(new List<string>(), new PropertyChangedCallback(Changed)));
private static void Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//e.NewValue here is your MyList in MainWindow.
}

How do I bind an `DependenyProperty` to another attachable `DependencyProperty`?

I want to have a singleton that holds some values S1 and S2 that I can bind to. The goal is to have some UIElements update when its value changes. The problem is that I want to use the value inside of a reused DataTemplate. That means that I cannot bind directly to a dependency property of the singleton but this has to be set outside.
To correctly pass updates the values have to be DependencyProperty. Because I dont know to which property I have to bind I created another attachable property AttProperty of the same type as the values. Now I tried to bind the S1 to AttProperty but this gives me an error:
Additional information: A 'Binding' cannot be set on the
'SetAttProperty' property of type 'TextBox'. A 'Binding' can only be
set on a DependencyProperty of a DependencyObject.
So how can I bind with an attachable DependencyProperty to another DependencyProperty?
Here is the code for the singleton I have so far (C#):
public class DO : DependencyObject
{
// Singleton pattern (Expose a single shared instance, prevent creating additional instances)
public static readonly DO Instance = new DO();
private DO() { }
public static readonly DependencyProperty S1Property = DependencyProperty.Register(
"S1", typeof(string), typeof(DO),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
public string S1
{
get { return (string)GetValue(S1Property); }
set { SetValue(S1Property, value); }
}
public static readonly DependencyProperty AttProperty = DependencyProperty.RegisterAttached(
"Att", typeof(string), typeof(DO),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender) );
public static void SetAttProperty(DependencyObject depObj, string value)
{
depObj.SetValue(AttProperty, value);
}
public static string GetAttProperty(DependencyObject depObj)
{
return (string)depObj.GetValue(AttProperty);
}
}
Here is the problematic thing (XAML):
<TextBox Name="Input" Text="" TextChanged="Input_TextChanged" local:DO.AttProperty="{Binding Source={x:Static local:DO.Instance}, Path=S1}" />
Update
With the changes of Bojin Li the errors go away. But one issue remains - if I now try to update the singleton with the help of the attached property like this:
<TextBox local:DO.Att="{Binding Source={x:Static local:DO.Instance}, Path=S1, Mode=TwoWay}" Text="{Binding Path=(local:DO.Att), RelativeSource={RelativeSource Self}, Mode=TwoWay}"/>
Why is the value not propagated to S1 in the singleton?
You need to implement INotifyPropertyChanged and wire the changes up to the dependency property changing.
public class DO : DependencyObject,INotifyPropertyChanged {
// Singleton pattern (Expose a single shared instance, prevent creating additional instances)
public static readonly DO Instance = new DO();
private DO() { }
public static readonly DependencyProperty S1Property = DependencyProperty.Register(
"S1", typeof(string), typeof(DO),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender,onS1Changed));
private static void onS1Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) {
DO item = d as DO;
if (item != null) item.OnPropertyChanged(new PropertyChangedEventArgs("S1"));
}
public string S1 {
get { return (string)GetValue(S1Property); }
set { SetValue(S1Property, value); }
}
public static readonly DependencyProperty AttProperty = DependencyProperty.RegisterAttached(
"Att", typeof(string), typeof(DO),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender,onAttChanged));
private static void onAttChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
DO item = d as DO;
if (item != null) item.OnPropertyChanged(new PropertyChangedEventArgs("Att"));
}
public static void SetAttProperty(DependencyObject depObj, string value) {
depObj.SetValue(AttProperty, value);
}
public static string GetAttProperty(DependencyObject depObj) {
return (string)depObj.GetValue(AttProperty);
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, e);
}
}
I don't think you named your Get/Set Accessors correctly for your attached Property as documented here. Try this instead:
public static readonly DependencyProperty AttProperty = DependencyProperty.RegisterAttached(
"Att", typeof(string), typeof(DO),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
public static void SetAtt(DependencyObject depObj, string value)
{
depObj.SetValue(AttProperty, value);
}
public static string GetAtt(DependencyObject depObj)
{
return (string)depObj.GetValue(AttProperty);
}
Example Binding:
<TextBlock local:DO.Att="{Binding Source={x:Static local:DO.Instance}, Path=S1}" Text="{Binding Path=(local:DO.Att),RelativeSource={RelativeSource Self}}">

Logically combine dependency properties

I'm using C# 4.0 and have created a DependencyObject MyView.
In MyView, I have two DependencyProperties, PropA and PropB, both are booleans.
I want a third DependencyProperty, PropC, also a bool, and simply put, should always give me (PropA || PropB).
What is the best way to accomplish this?
I was also thinking of making PropC a readonly DependencyProperty, but have read about issues with binding to readonly dp's (WPF ReadOnly Dependency Properties using MVVM)
You can use the Dependency Property changed callback for PropA and PropB to set the value for PropC (don't use the CLR property wrapper for the Dependency Properties as they are never guaranteed to be called).
If you have these three DP's
public static readonly DependencyProperty PropAProperty =
DependencyProperty.Register("PropA",
typeof(bool),
typeof(MyView),
new PropertyMetadata(false, PropAPropertyChanged));
public static readonly DependencyProperty PropBProperty =
DependencyProperty.Register("PropB",
typeof(bool),
typeof(MyView),
new PropertyMetadata(false, PropBPropertyChanged));
public static readonly DependencyProperty PropCProperty =
DependencyProperty.Register("PropC",
typeof(bool),
typeof(MyView),
new PropertyMetadata(false));
public bool PropA
{
get { return (bool)this.GetValue(PropAProperty); }
set { this.SetValue(PropAProperty, value); }
}
public bool PropB
{
get { return (bool)this.GetValue(PropBProperty); }
set { this.SetValue(PropBProperty, value); }
}
public bool PropC
{
get { return (bool)this.GetValue(PropCProperty); }
set { this.SetValue(PropCProperty, value); }
}
you can use the property changed callback like this
private static void PropAPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
MyView myView = source as MyView;
myView.OnPropChanged();
}
private static void PropBPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
MyView myView = source as MyView;
myView.OnPropChanged();
}
public void OnPropChanged()
{
PropC = PropA || PropB;
}
This way, you'll always update the value of PropC everytime PropA or PropB changes
Also, PropC doesn't need to be a DP, it can be a normal CLR property if you implement INotifyPropertyChanged. Then the implementation can look like this instead
public void OnPropChanged()
{
OnPropertyChanged("PropC");
}
public bool PropC
{
get
{
return PropA || PropB;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
You could also bind PropC to PropA and PropB with a MultiBinding. Let me know if you want an example of this as well
The linked web page is for an unusual situation, a "push" binding. That is, a one-way-to-source binding was attempted on the read-only property, not on another property trying to bind to it. By contrast, if you want your property to be bindable to other properties using a one-way binding expression on the other property, then you can use a read-only dependency property without any issues.
Edit:
Here is an example:
<Grid>
<Grid.Resources>
<local:MyObject x:Key="myObject" PropertyA="True" PropertyB="False"/>
</Grid.Resources>
<StackPanel DataContext="{StaticResource myObject}">
<CheckBox IsChecked="{Binding PropertyA}" Content="PropertyA"/>
<CheckBox IsChecked="{Binding PropertyB}" Content="PropertyB"/>
<CheckBox IsChecked="{Binding PropertyC, Mode=OneWay}" IsEnabled="False" Content="PropertyC"/>
</StackPanel>
</Grid>
and the dependency properties, one of which is read-only:
public class MyObject : DependencyObject
{
public bool PropertyA
{
get { return (bool)GetValue(PropertyAProperty); }
set { SetValue(PropertyAProperty, value); }
}
public static readonly DependencyProperty PropertyAProperty =
DependencyProperty.Register("PropertyA", typeof(bool), typeof(MyObject), new UIPropertyMetadata(false, OnPropertyAOrBChanged));
public bool PropertyB
{
get { return (bool)GetValue(PropertyBProperty); }
set { SetValue(PropertyBProperty, value); }
}
public static readonly DependencyProperty PropertyBProperty =
DependencyProperty.Register("PropertyB", typeof(bool), typeof(MyObject), new UIPropertyMetadata(false, OnPropertyAOrBChanged));
public bool PropertyC
{
get { return (bool)GetValue(PropertyCProperty); }
set { SetValue(PropertyCPropertyKey, value); }
}
private static readonly DependencyPropertyKey PropertyCPropertyKey =
DependencyProperty.RegisterReadOnly("PropertyC", typeof(bool), typeof(MyObject), new UIPropertyMetadata());
public static readonly DependencyProperty PropertyCProperty = PropertyCPropertyKey.DependencyProperty;
private static void OnPropertyAOrBChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var myObject = d as MyObject;
myObject.PropertyC = myObject.PropertyA || myObject.PropertyB;
}
}

Execute Silverlight 4 Command on Load

How do you implement a Silverlight 4 command to execute when the user control loads instead of being mapped to an explicit button click?
Or simply add a trigger in xaml for your UserControl:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<si:InvokeDataCommand Command="{Binding MyCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Create a DependencyProperty of type ICommand:-
#region public ICommand LoadedCommand
public ICommand LoadedCommand
{
get { return GetValue(LoadedCommandProperty) as ICommand; }
set { SetValue(LoadedCommandProperty, value); }
}
public static readonly DependencyProperty LoadedCommandProperty =
DependencyProperty.Register(
"LoadedCommand",
typeof(ICommand),
typeof(MainPage),
new PropertyMetadata(null));
#endregion public ICommand LoadedCommand
Also add a something to act as the command parameter:-
#region public object LoadedCommandParameter
public object LoadedCommandParameter
{
get { return GetValue(LoadedCommandParameterProperty) as object; }
set { SetValue(LoadedCommandParameterProperty, value); }
}
public static readonly DependencyProperty LoadedCommandParameterProperty =
DependencyProperty.Register(
"LoadedCommandParameter",
typeof(object),
typeof(MainPage),
new PropertyMetadata(null));
#endregion public object LoadedCommandParameter
Now set up its execution like this:-
public UserControl1()
{
InitializeComponent();
Loaded += UserControl1_Loaded;
}
void UserControl1_Loaded(object sender, RoutedEventArgs e)
{
if (LoadedCommand != null && LoadedCommand.CanExecute(LoadedCommandParameter))
{
LoadedCommand.Execute(LoadedCommandParameter);
}
}
Now if your ViewModel (has a command called StartStuff) then:-
<UserControl1 LoadedCommand="{Binding StartStuff}" .... >
Thats a load of code. The concise version without the code behind
public class LoadedBehaviour
{
public static ICommand GetLoadedCommand(DependencyObject dependencyObject)
{
return (ICommand)dependencyObject.GetValue(LoadedCommandProperty);
}
public static void SetLoadedCommand(DependencyObject dependencyObject, ICommand value)
{
dependencyObject.SetValue(LoadedCommandProperty, value);
}
public static Action GetLoadedCommandExecutor(DependencyObject dependencyObject)
{
return (Action)dependencyObject.GetValue(LoadedCommandExecutorProperty);
}
public static void SetLoadedCommandExecutor(DependencyObject dependencyObject, Action value)
{
dependencyObject.SetValue(LoadedCommandExecutorProperty, value);
}
public static readonly DependencyProperty LoadedCommandProperty = DependencyProperty.Register("LoadedCommand", typeof(ICommand), typeof(FrameworkElement), new PropertyMetadata(OnPropertyChanged));
public static readonly DependencyProperty LoadedCommandExecutorProperty = DependencyProperty.Register("LoadedCommandExecutor", typeof(Action), typeof(FrameworkElement), new PropertyMetadata(OnPropertyChanged));
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is FrameworkElement))
{
throw new ArgumentException("Loaded command can only be used on FrameworkElements");
var executor = GetLoadedCommandExecutor(d);
if(executor == null)
{
executor = () =>
{
var command = GetLoadedCommand(d);
command.Execute(e);
};
SetLoadedCommandExecutor(d, executor);
((FrameworkElement)d).Loaded += (obj, args) => executor();
}
}
}

WPF dependency property not recognized

I'm trying to overcome a limitation that doesn't allow me to bind to regular clr properties.
The solution I use uses custom dependency properties that in turn changes clr properties.
Here is the code
class BindableTextBox : TextBox
{
public static readonly DependencyProperty BoundSelectionStartProperty = DependencyProperty.Register("BoundSelctionStart", typeof(int), typeof(BindableTextBox),
new PropertyMetadata(new PropertyChangedCallback(BindableTextBox.onBoundSelectionStartChanged)));
private static void onBoundSelectionStartChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((TextBox)d).SelectionStart = (int)e.NewValue;
}
private static readonly DependencyProperty BoundSelectionLenghtProperty = DependencyProperty.Register("BoundSelectionLenght", typeof(int), typeof(BindableTextBox),
new PropertyMetadata(new PropertyChangedCallback(BindableTextBox.onBoundSelectionLenghtChanged)));
private static void onBoundSelectionLenghtChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((TextBox)d).SelectionLength = (int)e.NewValue;
}
public int BoundSelectionStart
{
get { return (int)GetValue(BoundSelectionStartProperty); }
set { SetValue(BoundSelectionStartProperty, value); }
}
public int BoundSelectionLenght
{
get { return (int)GetValue(BoundSelectionLenghtProperty); }
set { SetValue(BoundSelectionLenghtProperty, value); }
}
}
But when I try to bound something to BoundSelectionStart it says it says that I can only bind to DP.
<bindable:BindableTextBox Text="{Binding Name}" BoundSelectionStart="{Binding ElementName=slider1, Path=Value}" />
What is the problem?
You have a typo in the line:
public static readonly DependencyProperty BoundSelectionStartProperty = DependencyProperty.Register(...)
The first parameter should be "BoundSelectionStart" (2x e in Selection), not "BoundSelctionStart".

Resources