4

Assume this situation:
I have created a new control ("MyControl") with DependencyProperty "SuperValue".
Now, in XAML i set "SuperValue" to "TestValue":

<local:MyControl SuperValue="TestValue" />

This control has a ViewModel (DataContext).
I want to pass value of DependencyProperty (in this example "TestValue") to property in ViewModel.

How can I do this?

Assume that ViewModel of my control do something calculations, for example: User inputs name of country, and control give him a time which is currently there.

The problem is: How can I provide the result of calculation? Assume that this is public property "Results" in ViewModel. I want to create a property like "TextBox.Text", "ListView.SelectedItem" which provides a part of ViewModel data "to outside".

For example TextBox and Text property:

<TextBox Text={Binding GiveMeTextValue} />

In this case DP "Text" provides to outside a ViewModel property which currently stores inputted text.

I want to use my control in the same way.

Never
  • 335
  • 2
  • 7
  • 23

5 Answers5

3

I don't know whether I get your question right: You want to set a static non-bound value in XAML to a DependencyProperty of the control and set a property on the control's DataContext to this static value? There is something wrong about your concept if you need to do this, why don't you provide this value on the ViewModel in an according field and bind the DP of the control to this field?

However, what you can do get what you want:

Define a PropertyChangedCallback when you register the DP:

// Dependency Property
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register("Test", typeof(string),
typeof(MyControl), new FrameworkPropertyMetadata("123", new PropertyChangedCallback(OnTestChanged)));

In the OnTestChanged method, cast your DataContext to the type of your ViewModel and set the according value on the ViewModel to the new value:

private static void OnTestChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            MyControl c = d as MyControl;
            ViewModelType vm = c.DataContext as ViewModelType;
            vm.Property = e.New;
            Console.WriteLine(e.NewValue);
        }

Is that what you're asking for?

Marc
  • 12,706
  • 7
  • 61
  • 97
  • Yes, this should work but I don't really know that is this in 100 percent consistent with MVVM pattern and rules of components separation. – Never Nov 05 '12 at 22:48
  • It's not nice MVVM. The View definition (XAML) shouldn't contain "real data", that is data which is somehow processed in logic. What are you trying to do? Obviously you want to define a constant with value "TestValue" as you currently do in XAML. Why don't you define the constant in the ViewModel and just expose a public read only property which returns the constant? Then you can bind your control to this property of the ViewModel... – Marc Nov 06 '12 at 09:47
1

What about setting the MyDependencyProperty from the setter of property SomethingValueInDataContext.

EDIT

You can set the controls DependencyProperty where the control is used and not on its declaration. This will work (local is namespace where control resides) -

<Grid>
   <local:MyOwnControl MyDependencyProperty="{Binding Test}"/>
</Grid>

Same as like you can set the Width of the TextBox when you create an instance of it in xaml like this-

<TextBox Width="{Binding PropertyName}"/>
Rohit Vats
  • 79,502
  • 12
  • 161
  • 185
  • Sounds good, but i using MVVM patter so ViewModel (DataContext) rather does not know anything about View. – Never Oct 24 '12 at 15:16
  • Ok and can you elaborate why cannot you set your `MyDependencyProperty` from View? Where is this DP located? – Rohit Vats Oct 24 '12 at 15:18
  • It is almost good, but my control provides (to outside) result of operation conducted in ViewModel so I can't use your solution because I must set target point for MyDependencyProperty. Your solution would be good but it makes that property Dependency Property is unusable (no access to get or set). The ViewModel where property "Test" exists is ControlViewModel, no "ApplicationViewModel". – Never Oct 24 '12 at 16:00
1

Notice, the root of your xaml is UserControl and not MyOwnControl. UserControl is the base class of MyOwnControl; your property is not defined in the base class. This is why you cannot reference MyDependencyProperty from within the root element of the UserControl.

Using your example, you can switch the binding and get your desired effect.

<UserControl 
    x:Class="namespace.MyOwnControl"
    x:Name="root">
    <UserControl.DataContext>
         <local:ControlViewModel  
             Test={Binding MyDependencyProperty, ElementName=root}" />
    </UserControl.DataContext>
</UserControl>
  • 1
    It looks good but I have got error: A 'Binding' cannot be set on the 'Test' property of type 'ControlViewModel'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject. I can try make from ControlViewModel DependecyObject, but I do not realy know whether this is natural and consistent with MVVM? – Never Oct 30 '12 at 17:31
  • @Never: Yep. `ControlViewModel` has to extend `DependencyObject` and `Test` has to be a `DependencyProperty`. I always make my VM's DependencyObjects. It makes life so much easier. Also, DP bindings are the fastest bindings, so you get the advantage of speed. Its never a problem until it is (which is almost always never). –  Oct 30 '12 at 17:36
  • Ok, so i need to derive from DependencyObject and make "Test" DP in the same way like I have created MyDependencyProperty in my question? – Never Oct 30 '12 at 17:49
  • @Never: Correct. I'm not sure if this is the best solution for your design, but it is a viable one. You might just need to create a custom IMultiValueConverter and be done with it. –  Oct 30 '12 at 18:04
  • I did like You said but binding never updates MyDependencyProperty. It looks like that line: does not work. When I change the value of Test property, MyDependencyProperty event callback does not fire.I have uploaded code on: http://pastebin.com/gMSnfVV2 All test project: http://www.sendspace.com/file/75ypba Thank for try to help. – Never Oct 30 '12 at 20:44
  • I would strongly suggest NOT to make your Viewmodels DependencyObjects. among other drawbacks DependencyObjects have thread affinity and you can never do proper multithreading then. – MrDosu Nov 01 '12 at 12:34
  • @MrDosu: And so does the UI, so the same rules apply. Its absolutely not a big deal. –  Nov 01 '12 at 12:47
  • @Will: A quick example: A DependencyObject can only be accessed on the thread in which it was created. That means for your view interaction you need to create and initialize all viewmodels on the UI thread. This is ugly, can introduce nasty bugs and wont let you optimize the performance of your app. It's fine for small simple little apps, but for anything serious it's a killer. – MrDosu Nov 01 '12 at 13:50
  • Theres actually a SO question on the topic: http://stackoverflow.com/questions/291518/inotifypropertychanged-vs-dependencyproperty-in-viewmodel – MrDosu Nov 01 '12 at 13:55
  • @MrDosu: Uh, no, its not a killer at all. Practical experience speaking here. Your arguments--*Ugly*. Hardly, unless you consider WPF ugly. *Initialization on the UI thread.* Who says you have to? Its the same as anything in the UI--you offload to worker threads, then marshal to the UI thread when done. *Introduce[s] nasty bugs and wont let you optimize the performance of your app*. Not 1% different than standard multithreading issues in the UI. Drop the FUD, seriously. –  Nov 01 '12 at 13:56
  • @MrDosu: Yep. Advantages/disadvantages of each approach. Like everything else in life. –  Nov 01 '12 at 13:59
  • You can stick to your point no matter what, but its fact that there is a plethora of drawbacks when using DO's as viewmodels and there is zero when using POCOs. If you grew accustomed to do it your way it's fine and you can make it work. It's just terrible advice to give to people to fix an issue like this to take a huge dependency hit and a whole backlog of issues. – MrDosu Nov 01 '12 at 13:59
  • @MrDosu: I've done it both ways, and DO is the better way, unless you have specific requirements otherwise. Its as simple as that. You should expand your horizons a bit, and actually use it in smaller projects. You might be surprised. –  Nov 01 '12 at 14:01
  • @Will We will just have to agree to disagree. People who want to make an informed decision should just read the information provided and will see if they will take the plenty of boilerplate and magic strings for zero benefits ;p. – MrDosu Nov 01 '12 at 14:07
1

Since you are using a MVVM design paradigm all data should be relative to the ViewModel. So your DP should be set via the binding in your VM property.

If the test data is going to be used in Blend/VS designer you can check for that vs. Debug/Release... then do some sort of assignment to your property based off of that check for testing.

Albert Oldfield
  • 465
  • 5
  • 12
1

You could add a property to MyControl called InitialSuperValue that when set, sets the value of SuperValue. Then write some XAML like this:

<local:MyControl InitialSuperValue="TestValue" SuperValue="{Binding SuperValueInViewModel, Mode=OneWayToSource}" />
David Pond
  • 1,095
  • 9
  • 15
  • Yes, It will work, but writing "SuperValue="{Binding SuperValueInViewModel, Mode=OneWayToSource}"" every time when i use control is little crazy approach. – Never Nov 06 '12 at 18:04
  • Agreed. If you're looking to use this binding logic repeatedly (i.e. the control in within an items control which could have #####'s of instances -- or a similar scenario) then I'd recommend templating your control and setting the binding in the template. – Albert Oldfield Nov 06 '12 at 18:40