2016-09-13 2 views
0

Я изучаю WPF и шаблон MVVM, и я могу неправильно выполнять вещи.Пользовательский контроль в качестве компонентов. Нет доступа к родительскому окну

Я хочу построить компоненты для окон моего приложения, используя объекты User Control, где компоненты могут использоваться в Windows. Для примера у меня есть NewUnitUserControl, который я намерен использовать как единственный компонент для диалогового окна, а также как компонент для MainWindow.

NewUnitUserControl.xaml:

<UserControl x:Class="Sample.Views.UserControls.NewUnitUserControl" 
    ...     
    xmlns:local="clr-namespace:Sample.Views.UserControls"> 

    <Grid> 
     <StackPanel Orientation="Vertical"> 
      <Border> 
       <StackPanel Orientation="Horizontal"> 
        <Label>Name:</Label> 
        <TextBox Text="{Binding Unit.Name}" Width="136"/> 
       </StackPanel> 
      </Border> 
      <Border HorizontalAlignment="Center"> 
       <StackPanel Orientation="Horizontal"> 
        <Border> 
         <Button Command="{Binding CreateUnitCommand}">Create</Button> 
        </Border> 
        <Border> 
         <Button Command="{Binding CancelCommand}">Cancel</Button> 
        </Border> 
       </StackPanel> 
      </Border> 
     </StackPanel> 
    </Grid> 
</UserControl> 

NewUnitUserControl.xaml.cs:

namespace Sample.Views.UserControls 
{ 
    public partial class NewUnitUserControl : UserControl 
    { 
     public NewUnitUserControl() 
     { 
      InitializeComponent(); 
      NewUnitViewModel nuvm = new NewUnitViewModel(); 
      DataContext = nuvm; 
      if (nuvm.CloseAction == null) 
      { 
       var window = Window.GetWindow(this); // window evaluates to null 
                // after this line. 
       nuvm.CloseAction = new Action(window.Close); 
      } 
     } 
    } 
} 

NewUnitViewModel.cs

namespace Sample.ViewModels 
{ 
    internal class NewUnitViewModel : INotifyPropertyChanged 
    { 
     //Properties 
     private Unit _unit; 
     public Unit Unit 
     { 
      get { return _unit; } 
      set { _unit = value; OnPropertyChanged("Unit"); } 
     } 

     public Action CloseAction { get; set; } 

     private ICommand _createUnitCommand; 
     public ICommand CreateUnitCommand 
     { 
      get 
      { 
       if (_unitUpdateCommand == null) 
        _unitUpdateCommand = new RelayCommand(param => CreateUnit(), param => true); 
       return _unitUpdateCommand; 
      } 
     } 

     private ICommand _cancelCommand; 
     public ICommand CancelCommand 
     { 
      get 
      { 
       if(_cancelCommand == null) 
        _cancelCommand = new RelayCommand(param => Cancel(), param => true); 
       return _cancelCommand; 
      } 
     } 

     //Constructor 
     public CreateUnitViewModel() 
     { 
      _unit = new Models.Unit(); 
     } 

     //Methods 
     public void CreateUnit() 
     { 
      Debug.Assert(false, String.Format("{0} was created.", Unit.Name)); 
     } 

     public void Cancel() 
     { 
      this.CloseAction(); 
     } 

     #region INotifyProperty Members 

     public event PropertyChangedEventHandler PropertyChanged; 

     private void OnPropertyChanged(String propertyName) 
     { 
      PropertyChangedEventHandler handler = PropertyChanged; 

      if (handler != null) 
      { 
       handler(this, new PropertyChangedEventArgs(propertyName)); 
      } 
     } 

     #endregion 
    } 
} 

NewUnitDialogWindow.xaml:

<Window x:Class="Sample.Views.NewUnitWindow" 
     ... 
     xmlns:local="clr-namespace:Sample.Views" 
     xmlns:controls="clr-namespace:Sample.Views.UserControls" 
     > 
    <controls:NewUnitUserControl /> 
</Window> 

NewUnitDialogWindow.xaml.cs

namespace Sample.Views 
{ 
    public partial class NewUnitWindow : Window 
    { 
     public NewUnitWindow() 
     { 
      InitializeComponent(); 
     }  
    } 
} 

Полный Источник: GitHub

Проблема я сразу же сталкивается в том, что с моей реализации, я не могу получить доступ к родительскому окну пользовательского элемента управления, используя решение here (см. Комментарий в NewUnitUserControl.xaml.cs). Я ожидаю, что корень моих проблем заключается в том, что я неправильно понимаю шаблон MVVM.

+0

'NewUnitViewModel cuvm = new NewUnitViewModel(); DataContext = nuvm; 'это запах кода. Ваш пользовательский элемент управления должен вести себя как элемент управления. Элементы управления не устанавливают свой DataContext. Создает ли TextBox экземпляр TextBoxViewModel и устанавливает его как DataContext? Это помешало бы вам отказаться от этого. – Will

+0

@ Пока я вижу вашу точку зрения. Я не вижу, как создать автономный многоразовый «компонент» с помощью пользовательского элемента управления, не устанавливая DataContext User Control. Что-то не так с моей концепцией того, что составляет пользовательский контроль? Есть ли другой способ сделать это? – ZackDeRose

+0

Лучший способ представить состояние вашего приложения через график моделей и моделей. Корнем этого графа объектов является DataContext главного окна. Элементы управления привязаны к свойствам этого корня. Например, он может иметь наблюдаемую коллекцию моделей детского просмотра. Сбор привязан к элементу ItemsSource ListView. Определены DataTemplates с ItemType для каждого типа дочерних элементов, и в них помещается UserControl, предназначенный для этого дочернего элемента. Теперь, когда ListView ищет DataTemplate типа NewUnitViewModel, он находит шаблон и poof! – Will

ответ

0

Я думаю, проблема в том, что пользовательский элемент управления не знает, к какому родительскому окну еще нет. Попробуйте перейти в нагруженное событие:

private Window _parentWindow = null; 
public NewUnitUserControl() 
{ 
    InitializeComponent(); 
    Loaded += (s, e) => 
    { 
     _parentWindow = Window.GetWindow(this); 
     /// whatever you are going to do with parent window 
    } 
} 

Вы также можете отправить сообщение из командного действия этой кнопки, используя что-то вроде IEventAggregator из набора инструментов Prism или IMessenger (я думаю, что это то, что она называется) из MvvmToolkit.

+0

Это делает мое диалоговое окно закрытым нажатием кнопки «Отмена». Благодаря! При реализации этого как компонента для моего MainWindow я хочу, чтобы кнопка «Отменить» «выгрузила» NewCharacterUserControl из моего MainWindow. Должен ли я рассмотреть возможность перемещения функциональных возможностей кнопки «Отмена» в представлении родителя (или «Просмотр модели») для поддержки различного поведения при нажатии кнопки? – ZackDeRose

+0

Это своего рода расплывчатый, но я бы управлял пользовательским управлением своими собственными действиями, а mainwindow управлял своим собственным. Пользовательский элемент управления обрабатывает данные и сигнализирует, что они закончены или отменены. Главное окно получит сигнал и сделает то, что ему нужно сделать, обновить главное окно, сохранить данные, закрыть диалог и т. Д. –

0

То, как вы пытаетесь получить доступ к родительскому окну, не кажется мне знакомым.

var window = Window.GetWindow(this);

Пожалуйста, попробуйте ввести следующий код. Я отредактировал ваш, так что просто замените его :)

public NewUnitUserControl() 
{ 
    InitializeComponent(); 
    CreateUnitViewModel cuvm = new CreateUnitViewModel(); 
    DataContext = nuvm; 
    if (nuvm.CloseAction == null) 
    { 
     //var window = this.ParentForm; // window evaluates to null 
     //          // after this line. 
     if(this.ParentForm != null) 
     cuvm.CloseAction = new Action(this.ParentForm.Close); 
    } 
} 
+0

Когда я вставлял этот код, я получил ошибку компилятора: «NewUnitUserControl» не содержит определения для «ParentForm», и метод расширения «ParentForm», принимающий первый аргумент типа «NewUnitUserControl», не найден (вы отсутствует директива using или ссылка на сборку?) Я что-то упустил? – ZackDeRose

+0

Это не должно быть ошибкой из-за отсутствия импорта. Вы используете правильные DLL-ссылки? В качестве ссылки вы должны иметь как минимум System.Windows.Forms. Также вы должны использовать его следующим образом: «using System.Windows.Forms;» Является ли ваш UserControl локализованным внутри этого пространства имен или это пользовательский класс, который вы создали? Также имейте в виду, что ваш NewUnitUserControl должен быть помещен в форму. Другой проблемой может быть ваша версия .NET Framework. Я использую версию 4+. В некоторых более ранних версиях это может быть не реализовано. Надеюсь, я дал тебе несколько намеков. – bitQUAKE

Смежные вопросы