Използване на DataContext на родителя (WPF - Динамично обвързване на командата на менюто)

Прегледах тази мрежа и Google и решенията не работят за мен.

Имам команда за ViewModel на UserControl. Е, потребителският контрол има ItemsControl, обвързан с ObservableCollection. Вътре в DataTemplate на ItemsControl.ItemTemplate имам бутон и искам да използвам командата. Не мога да обвържа командата, защото вътре в DataTemplate, datacontext не е ViewModel, а елемент от ObservableCollection.

Въпросът е: Как мога да свържа бутона към командата, ако загубих родителския контекст на данни?

Мисля, че това трябва да има лесно решение, защото смятам, че това е често срещан проблем.

Представете си тази сцена:

Имате елемент от ListBox с observableCollection като ItemsSource, така че използвате шаблон за данни вътре в ListBox за всеки елемент в колекцията. Е, искате да изтриете избрания елемент и поставяте бутон във всеки ред за тази задача. Как правиш това?

В MVP мога да направя това в събитието за щракване на бутона:

Button but = e.Source as Button;

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

Накратко. Изпращате контекста на данните на реда (избрания елемент) на представящия.

Но как мога да го направя по mvvm начина? Тъй като трябва да използвам команда, но не мога да присвоя командата на бутона, защото бутонът не знае нищо за ViewModel (където съществува командата).

Както можете да видите, бутонът трябва да съществува в шаблона за данни, тогава контекстът на данните вече не е ViewModel... Ето защо имам нужда от достъп до DataContext на родителя за достъп до командата.

Надявам се, че разбирате проблема ми по-добре.

Благодаря ти.


person Jesus Rodriguez    schedule 15.06.2009    source източник


Отговори (4)


Ако искате мръсно решение, нарушаващо MVVM, тогава задайте Tag="{Binding}" на бутона и управлявайте събитието Click. В манипулатора на събития извикайте командата на вашия ViewModel.

person geofftnz    schedule 15.06.2009
comment
:P. Търся добро решение, без да нарушавам mvvm :P, но благодаря, това е решение :P - person Jesus Rodriguez; 16.06.2009
comment
Аз съм SL разработчик, така че разбиването на mvvm е норма за курса :) - person geofftnz; 16.06.2009
comment
Това каза, че използвам полу-MVVM модел в моето приложение и е добре. - person geofftnz; 17.06.2009
comment
Накрая използвах това решение, но без да използвам свойството Tag. Просто щракнете върху събитието и изпълнете командите, много мръсно, но трябва да продължа. Ще опитам друго решение другия ден. - person Jesus Rodriguez; 19.06.2009

Използвайте обвързването по-долу за командата на вашия бутон:

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

Това ще му каже да намери вашия UserControl и да използва неговия DataContext.

person Eddie Deyo    schedule 15.06.2009
comment
Това е решението, което намерих, но не работи за мен. Ако a put: Command={Binding DataContext.CommandName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}} Това казва, че AncestorType трябва да бъде посочен за RelativeSource в режим FindAncestor. Къде е проблема? Благодаря за отговора. - person Jesus Rodriguez; 16.06.2009
comment
Току-що опитах това на проба и ми свърши работа. Звучи като синтактична грешка. Можете ли да копирате и поставите XAML на вашия бутон? - person Eddie Deyo; 16.06.2009
comment
О, няма грешка, VS го маркира, но се компилира. Опитвам се да знам с нещо просто. Поставям елемент Tag в потребителския контрол и искам да го отпечатам en в заглавката на елемент от менюто: ‹MenuItem Header={Binding Tag, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}} /›. Но визуалното студио казва: Не може да се намери източник за обвързване с препратка „RelativeSource FindAncestor, AncestorType='System.Windows.Controls.UserControl', AncestorLevel='1''. BindingExpression:Path=Tag; DataItem=null; целевият елемент е 'MenuItem' (Name=''); целевото свойство е „Header“ (тип „Object“ - person Jesus Rodriguez; 16.06.2009
comment
Вместо да поставите UserControl за вашия тип, опитайте да поставите типа на вашия контрол. Така че бихте имали {x:Type ctrl:MyControl}, ако вашата контрола се нарича MyControl и сте съпоставили xmlns:ctrl към неговото пространство от имена. - person Eddie Deyo; 16.06.2009
comment
+1! Страхотно, това най-накрая проработи след неуспешен подход с ElementName. - person Sören; 28.02.2011
comment
Страхотно, работи като чар! Като странична бележка: ако все пак искате да запазите контекста на данните на бутона, за да преминете към обвързването на командата, използвайте CommandParameter={Binding Path=.} По този начин обектът на параметъра е контекстът на контролата. - person Simon Mattes; 18.11.2014
comment
той казва, че myusercontrol не съществува - person Isuru Herath; 09.05.2015

Добре, тогава какво ще кажете за модифициране на вашия клас на елемент от данни, така че да има свойство, препращащо към целия изглед на модела?

Ако вашият ItemsSource е от тип ObservableCollection<DataItem>, тогава променете типа DataItem по следния начин:

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

    private ModelView modelView;

    public ModelView ModelView
    {
        get
        {
            return modelView;
        }
    }

    public DataItem(ModelView modelView)
    {
        this.modelView = modelView;
    }
}
person Dmitry Tashkinov    schedule 17.06.2009
comment
Можете ли да обясните това отново? С моя английски не мога да разбера какво се опитвате да ми обясните. съжалявам - person Jesus Rodriguez; 17.06.2009
comment
Добавено е по-подробно обяснение. - person Dmitry Tashkinov; 17.06.2009

RelativeSource работи, но не мисля, че е правилно да позволявам на контролите да се движат в свойствата на другия. Странно е, че бутонът, поставен в изглед на елемент, прави нещо с външен източник на данни, а не с обвързания елемент. Може да се наложи да прегледате дизайна на програмния код.

person Dmitry Tashkinov    schedule 15.06.2009
comment
Харесвам отговора ви, но имам нужда от това за това решение. Имам елемент контрол с решетка вътре, всеки елемент е бележка. Е, имам контекстно меню в решетката, което има някои опции за избраната бележка. Всяка опция е команда, но контекстът на данните на менюто е действителната бележка, а не моделът на изглед. Не мога да сложа менюто отвън, защото имам нужда от меню за всеки елемент. - person Jesus Rodriguez; 16.06.2009
comment
Хм, да, не смятам, че това е идеално решение, но можете да поставите своя обект ModelView като статичен ресурс в елемент Window.Resources, може би с помощта на ObjectDataProvider, и след това да го препратите чрез статично разширение във всяко обвързване на данни от всяка част на прозореца. - person Dmitry Tashkinov; 16.06.2009
comment
Съгласен съм -- винаги съм се чувствал като хак, когато съм използвал това решение -- но хак, който работи. ;) - person Eddie Deyo; 16.06.2009