Using the parent's DataContext (WPF - Dynamic Menu Command Binding)
Asked Answered
K

4

32

I looked over this web and google and the solutions didn't work for me.

I have a command on the ViewModel of a UserControl. Well, The usercontrol have a ItemsControl binded to a ObservableCollection. Inside the DataTemplate of the ItemsControl.ItemTemplate I have a button and I want to use the command. I can't bind the command because inside the DataTemplate, the datacontext is not the ViewModel but an item of the ObservableCollection.

The question is: How can I bind the button to the command if a lost the parent datacontext?

I think that this need to have an easy solution because I think that this is a common problem.

Imagine this sceneario:

You have a ListBox item with an observableCollection as the ItemsSource, so you are using a datatemplate inside the ListBox for every element in the collection. Well, you want to delete the selected item and you put a button in every row for that job. ¿How do you do that?

In MVP, I can do this in the click event of the button:

Button but = e.Source as Button;

if (but != null)
      Presenter.ActualNote = but.DataContext as Note;

In short. You send the datacontext of the row (the selected item) to the presenter.

But, how can I do it in the mvvm way? Because I need to use a command but I can't assign the command to the button because the button does know nothing about the ViewModel (where the command exists).

As you can see, the button has to exist inside the datatemplate, then the datacontext is not the ViewModel anymore.... There is why I need to access to the parent's DataContext, for access to the command.

I hope that you understand my problem better.

Thank you.

Koniology answered 15/6, 2009 at 20:2 Comment(0)
S
5

If you want a dirty, MVVM-breaking solution, then set the Tag="{Binding}" on the button and handle the Click event. In the event handler, call the command on your ViewModel.

Sheepwalk answered 15/6, 2009 at 21:10 Comment(4)
:P. I'm looking for a good solution without breaking mvvm :P, but thanks, it's a solution :PKoniology
I'm a SL developer, so breaking mvvm is par for the course :)Sheepwalk
That said, I'm using a semi-MVVM pattern in my app and it's fine.Sheepwalk
Finally, I used this solution, but without using the Tag property. Simply click event and execute the commands, veeeeery dirty, but I need to continue. I will try another solution other day.Koniology
S
91

Use the binding below for your button's command:

{Binding DataContext.CommandName, 
         RelativeSource={RelativeSource FindAncestor, 
                         AncestorType={x:Type MyUserControl}}}

This will tell it to find your UserControl and use its DataContext.

Scraggy answered 15/6, 2009 at 20:9 Comment(7)
That's is the solution I found but It doesn't work for me. If a put: Command="{Binding DataContext.CommandName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" Its says that AncestorType should be specified for RelativeSource on the FindAncestor mode. Where is the problem? Thanks for the answer.Koniology
I just tried this in a sample and it worked for me. It sounds like a syntax error. Can you copy and paste your button's XAML?Scraggy
Oh, there is no error, VS marked it but it compiles. Im trying know with something simple. I put a Tag element in the usercontrol and I want to print it en in the header of a menuitem: <MenuItem Header="{Binding Tag, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />. But, visual studio say: Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.UserControl', AncestorLevel='1''. BindingExpression:Path=Tag; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'Header' (type 'Object'Koniology
Instead of putting UserControl for your type, try putting the type of your control. So you would have {x:Type ctrl:MyControl}, if your control was called MyControl and you've mapped xmlns:ctrl to its namespace.Scraggy
+1! Great, this worked finally after an approach with the ElementName failed.Commixture
Great, works like a charm! As a side-note: if you still wish to preserve the data context of the button to pass to the command binding, use CommandParameter="{Binding Path=.}" That way the parameter object is the context of the control.Parrisch
it says myusercontrol is not existingErsatz
S
5

If you want a dirty, MVVM-breaking solution, then set the Tag="{Binding}" on the button and handle the Click event. In the event handler, call the command on your ViewModel.

Sheepwalk answered 15/6, 2009 at 21:10 Comment(4)
:P. I'm looking for a good solution without breaking mvvm :P, but thanks, it's a solution :PKoniology
I'm a SL developer, so breaking mvvm is par for the course :)Sheepwalk
That said, I'm using a semi-MVVM pattern in my app and it's fine.Sheepwalk
Finally, I used this solution, but without using the Tag property. Simply click event and execute the commands, veeeeery dirty, but I need to continue. I will try another solution other day.Koniology
P
3

Ok, then what about modifying your data item class so that it has a property referencing to the whole model view?

If your ItemsSource is of type ObservableCollection<DataItem> then modify DataItem type like this:

public class DataItem
{
    public BusinessObject Value { get; set; }

    private ModelView modelView;

    public ModelView ModelView
    {
        get
        {
            return modelView;
        }
    }

    public DataItem(ModelView modelView)
    {
        this.modelView = modelView;
    }
}
Property answered 17/6, 2009 at 6:7 Comment(2)
Can you explain that again? With my english I can't understand what are you trying to explain to me. Sorry.Koniology
Added a more detailed explanation.Property
P
2

RelativeSource works, but I don't think it's right to let controls to prowl across each other's properties. It is strange that button placed inside an item view does something with an outer data source rather than with the bound item. You might need to review the program code’s design.

Property answered 15/6, 2009 at 20:58 Comment(3)
I like your answer but I need that for this solution. I have a itemscontrol with a grid inside, every item is a note. Well, I have a context menu in the grid that have some options about the "selected" note. Every option is a command but the menu's datacontext is the actual note an not the viewmodel. I cant put the menu outside because I need the menu for each item.Koniology
Hm, yes, don't think it's an ideal solution, but you can place your ModelView object as a static resorce into a Window.Resources element, maybe using ObjectDataProvider, and then reference it by Static extension inside any data binding from any part of the window.Property
I agree -- it's always felt like a hack when I've used this solution -- but a hack that works. ;)Scraggy

© 2022 - 2024 — McMap. All rights reserved.