简体   繁体   中英

Creating UserControl via MVVM pattern - DataContext and binding to parent

I use MVVM pattern so my own control contains View and ViewModel.
ViewModel is connected with View by DataContext property. This makes problems with binding. Why?

Assume this situation:
I created new user control - for example - "SuperTextBox". It have a property "SuperValue".
And now I do something like that:

<Window>
    <Window.DataContext>
        <vm:WindowViewModel/>
    </Window.DataContext>

    <local:SuperTextBox SuperValue="{Binding Test}"/>
</Window>

I thought that "binding process" joins SuperTextBox.SuperValue with Window.DataContext.Test, but no, 'binding process" joins SuperTextBox.SuperValue with SuperTextBox.DataContext.Test what is for me unnatural and misleading.

Other controls like "TextBox" I can use in above way because they do not have their DataContext.

How can I use MVVM pattern to creating UserControls keeping natural binding (to DataContext of parent control) ?

Edit:

I got many answers how binding to parent, but I know this earlier. The problem is - how can I create UserControl via MVVM patern (having ViewModel) and stay natural binding - default to parent DataContext.

I want to have ViewMoldel and still can binding like this:

<local:SuperTextBox SuperValue="{Binding Test}"/>

Is it possible?

All bindings applied on any control always first look for the binding in its DataContext . In case the DataContext is not set for the control, then it walks up the Visual Tree upto its parent unless it finds the DataContext.

Even if you set the DataContext on your textBox to some value different than of Window's DatContext, it will always search for property Test on that particular DataContext and not on your Window's DataContext .

<TextBox>
   <TextBox.DataContext>
      <vm:ViewModelForTextBox/>
   </TextBox.DataContext>
   <TextBox.Text>
      <Binding Path="Test"/>
   </TextBox.Text>
</TextBox>

Now, xaml will look for Test property in class ViewModelForTextBox instead in class WindowViewModel and if Test property is not found in class ViewModelForTextBox , binding will fail silently and won't look up to the Window's DataContext class.

In case you still want to set the DataContext for your Custom UserControl but still want to bind to the parent's (Window) dataContext, you have to use the RelativeSource MarkupExtension in your bindings like this -

<local:SuperTextBox SuperValue="{Binding Path=DataContext.Test,
                                RelativeSource={RelativeSource Mode=FindAncestor,
                                                 AncestorType={x:Type Window}}}">

Refer to the MSDN article here for more clarification.

you should post your SuperTextBox code, because there is your error.

usually you create a usercontrol with a dependency property - in your case "SuperValue" - and now the most important thing you do not set the datacontext of your SuperTextBox to it self.

you have to use elementname binding within your SuperTextBox to bind to "SuperValue"

 <SuperTextBox x:Name="uc">
   <TextBox Text="{Binding ElementName=uc, Path=SuperValue}/>
 </superTextBox>

if you do this that way - your

 <local:SuperTextBox SuperValue="{Binding Test}"/>

should work and should bind to the Test Property of your vm:WindowViewModel. thats the only way to write the binding like above.

EDIT: if you wanna create a viewmodel for your usercontrol, let say SuperTextViewmodel. then it would have a property "SuperValue". now you cant set the datacontext twice so i would suggest you have to add a Property to your WindowViewmodel of type SuperTextViewmodel and handle the properties like you want.

your binding the looks like this

 <local:SuperTextBox DataContext="{Binding MySuperTextViewmodelInstanceOnWindowViewmodel}"/>

i would go with my first part of the answer :) i always say a view need a viewmodel but a usercontrol dependency properties.

You need to "Look up" the datacontext of it's window ancestor. Your binding would look like this:

<local:SuperTextBox SuperValue="{Binding Path=DataContext.Test, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">

I feel oddly answering on my question but...
In my own control i did something like that:

<UserControl>
    <Grid>
        <Grid.DataContext>
             <vm:UserControlViewModel />
        </Grid.DataContext>
        // here realy code of control
    </Grid>
</UserControl>

Now I can use "natural" binding outside of control and in control. :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM