12
<Controls:MyControl Mode="Mode1" Value="{Binding Path=Something}" />

The problem I have is that binding happens before Mode property is being set when I use this control in a data template of a ListView.

How do I make sure that Mode is always set before Value binding?

lahsrah
  • 9,013
  • 5
  • 37
  • 67
  • It depends on what you mean by "Mouse is always set before". I guess `Mode` also has a default value. Do you want the binding to happend when `Mode` is not null? – Fredrik Hedblad Oct 05 '11 at 20:46
  • Seems like Value Coercion may be an appropriate solution for you. – vcsjones Oct 05 '11 at 20:57
  • @Meleak I am talking about Mode property of my control not mouse. The problem is Binding is happening before Mode property is set. And that affects how the control behaves. I want Mode to be set first. Mode has a default value so its never null, but i want to choose the mode of the control before binding happens. – lahsrah Oct 05 '11 at 21:56
  • 1
    Have you tried raising the property changed event for your property "Something" when Mode of your control get set? – Rohit Vats Oct 06 '11 at 06:35
  • @RV1987, it doesn't seem like a very good idea... the control isn't supposed to know anything about "Something" – Thomas Levesque Oct 06 '11 at 12:35
  • @Thomas Levesque - I know this isn't a good approach. But i can't think of any better than this and it will serve the purpose. So, just post in case sylon doesn't want to religiously follow MVVM. :) – Rohit Vats Oct 06 '11 at 13:50
  • @RV1987, it's not about MVVM, it's about reusability of the control. What if you want to bind Value to something else? – Thomas Levesque Oct 06 '11 at 15:06
  • @Thomas Levesque - Please look at my answer below. Its independent of the property its binded to. I am fetching the property using BindingOperations. – Rohit Vats Oct 06 '11 at 15:15
  • @RV1987, that's better, but it still won't work ;) (see my comment on your answer) – Thomas Levesque Oct 06 '11 at 15:52
  • It seems like the only way to solve this is to use a non-default constructor. And that is only supported in XAML 2009 with construct parameters using the tags. http://www.wpftutorial.net/XAML2009.html Which is only supported for loose xaml files in Visual Studio 2010. Looks like I will have to implement a workaround to this for now. – lahsrah Oct 12 '11 at 05:14

3 Answers3

3

What you could do is delay the binding, so you can be (almost) sure the mode value is set till then. There's a delay binding property in .net 4.5. Here's an article on how to simulate that in .net 4.0 Delay property on Binding from .Net 4.5 in .Net 4.0

I personally would implement that in a viewModel (MVVM), where this kind of issue is rather simple to resolve. Make two properties Mode and Something. When mode changes it should trigger that the "Something" property was changed also ( via INotifyPropertyChanged Interface).

class MyViewModel : INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
      if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    private string _mode;

    public string Mode
    {
      get { return _mode; }
      set
      {
        _mode = value;
        OnPropertyChanged("Mode");

        // OnPropertyChanged("Something");
        // or build up via a method:
        // Something = DetermineFromMode(Mode);
      }
    }

    private string _something;

    public string Something
    {
      get { return _something;  }
      set
      {
        _something = value;
        OnPropertyChanged("Something");
      }
    }
  }
Community
  • 1
  • 1
SvenG
  • 5,155
  • 2
  • 27
  • 36
  • Delay(in .Net4.5) is used when we want to delay the update of source from the target but here the situation is vice-versa though!! – Rohit Vats Oct 06 '11 at 13:48
2

(I know this is old but I ran into this problem today so I was forced to do some investigation)

It appears that binding occurs in the order the DependencyProperties are defined.

For instance, in MyControl.cs

public int Mode { get => (int)GetValue(ModeProperty); set => SetValue(ModeProperty, value); }
public static readonly DependencyProperty ModeProperty
    = DependencyProperty.Register(nameof(Mode), typeof(int), typeof(MyControl),
        new FrameworkPropertyMetadata(new PropertyChangedCallback(Mode_Changed)));

public string Result { get => (string)GetValue(ResultProperty); set => SetValue(ResultProperty, value); }
public static readonly DependencyProperty ResultProperty
    = DependencyProperty.Register(nameof(Result), typeof(string), typeof(MyControl),
        new FrameworkPropertyMetadata(new PropertyChangedCallback(Result_Changed)));

private static void Mode_Changed(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    //Mode changed. Update display.
}

private static void Result_Changed(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    //Result changed. Update display.
}

And the XAML:

<controls:MyControl Result="You passed!"
                    Mode="3" />

Because Mode is defined as a dependency property before Result, Mode will be set first. The order in which you assign the values in the XAML is ignored.

So in theory, to answer your question, make sure the Mode property is defined first. This solved my issue.

J Wilson
  • 21
  • 1
2

Have you tried raising the property changed event for your property "Something" when Mode of your control get set? You can get the property "Something" in you control like this -

Binding binding = BindingOperations.GetBindingExpression(this, this.Value).ParentBindingBase;
String propertyToRefresh = binding.Path.Path;

(this.DataContext as ViewModel).OnPropertyChange(propertyToRefresh);

I am assuming that the DataContext of your Control is your ViewModel's instance which implements INotifyPropertyChangedInterface.

In case your Viemodel class OnPropertyChange method is not public or you don't have reference to your ViewModel class in your control. You can simply call the UpdateTarget() on your BindingExpression like this (as suggested by Thomas Levesque) -

BindingExpression binding = BindingOperations.GetBindingExpression(this, this.Value);
binding.UpdateTarget();
Rohit Vats
  • 79,502
  • 12
  • 161
  • 185
  • OnPropertyChanged is usually not public; a better option would be to force the binding to refresh, by calling UpdateTarget on the BindingExpression – Thomas Levesque Oct 06 '11 at 15:53
  • Yeah!! I have updated my answer. Thanks a lot, i almost missed this part. – Rohit Vats Oct 06 '11 at 16:13
  • I can't use property changed event because based on the Mode property, whatever is bound to the Value can get thrown away and wont ever be recoverable. – lahsrah Oct 10 '11 at 00:54
  • I didn't get you. Why Binding for value will be thrown away? PropertyChanged will only update the target as per the bindings!! – Rohit Vats Oct 10 '11 at 06:57
  • The way my control works is that if the Mode=ModeX then you should never be binding to Value property and thats why it is thrown away. it is probably not the most ideal way to implement a control but its too late as I am using this control extensively. – lahsrah Oct 12 '11 at 05:16
  • Forcing binding to refresh is a good idea. But in my case it didn't work because calling `GetBindingExpression` in `DependencyPropertyChangedEventHandler` for property that should be updated first returned `null`. The workaround is to return from handler if `BindingExpression` is null - but it's not a pefrect solution. – N. Kudryavtsev Jul 23 '18 at 12:48