2014-01-14 2 views
2

Я разрабатываю приложение для Windows Store с использованием шаблона MVVM (без рамки, только raw MVVM).Обработка событий в приложениях Windows Store с использованием MVVM

У меня есть пользовательское управление DropboxFileplanUserControl.xaml, у которого есть соответствующая модель просмотра DropboxFileplanViewModel.cs. DropboxFileplanUserControl встроен в MainPage.xaml и MainPage.xaml имеет ассоциированную модель просмотра, MainPageViewModel.cs.

Мой вопрос: Как я могу определить и поднять событие в DropboxFileplanViewModel.cs и обрабатывать его в MainPageViewModel.cs? Предположим, что событие, которое будет поднято, называется ImageLoaded.

EDIT: я добавил следующие конкретные фрагменты кода ...

DropboxFileplanUserControl.xaml

<UserControl 
x:Class="PhotoBox.Controls.DropboxFileplanUserControl" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:local="using:PhotoBox.Controls" 
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
xmlns:viewModels="using:PhotoBox.ViewModels" 
xmlns:triggers="using:WinRT.Triggers" 
mc:Ignorable="d" 
d:DesignHeight="300" 
d:DesignWidth="200"> 

<UserControl.DataContext> 
    <viewModels:DropboxFileplanViewModel /> 
</UserControl.DataContext> 

<Grid> 
    <ListBox> 
    <!-- 
     ... 
     ... 
     Here I define a ListBox and use interaction triggers to bind the SelectionChanged event to FileplanSelectionChangedCommand on the ViewModel --> 
     <triggers:Interactions.Triggers> 
      <triggers:EventTrigger EventName="SelectionChanged"> 
       <triggers:InvokeCommandAction Command="{Binding FileplanSelectionChangedCommand}" PassEventArgsToCommand="True" /> 
      </triggers:EventTrigger> 
     </triggers:Interactions.Triggers> 
    </ListBox> 
</Grid> 

DropboxFileplanViewModel.xamlПримечание: Я раздели много кода из этого фрагмента

public class DropboxFileplanViewModel : ViewModel 
{ 
    public DelegateCommand FileplanSelectionChangedCommand { get; set; } 

    public DropboxFileplanViewModel() 
    { 
     FileplanSelectionChangedCommand = new DelegateCommand(FileplanSelectionChanged); 
    } 

    private void FileplanSelectionChanged(object parameter) 
    { 
     var args = (SelectionChangedEventArgs) parameter; 

     // Some other stuff is done here but ultimately, 
     // GetImageFile is called 

    } 

    private async void GetImageFile(MetaData file) 
    { 
     // Stuff is done here to get the image 

     // ******************************       
     // Here I want to raise the event 
     // ******************************  
    } 
} 

DropboxFileplanUserControl вкладывается в MainPage.xaml следующим образом ...

MainPage.xaml

<controls:DropboxFileplanUserControl 
      Grid.Row="0" 
      DataContext="{Binding FileplanControl}" 
      Visibility="{Binding IsOpen, Converter={StaticResource BooleanToVisibilityConverter}}" 
      IsEnabled="{Binding IsOpen}" 
      <!-- *** Here I need to access the ImageLoaded event and bind it to a command in MainPageViewModel.cs *** --> 
      /> 

Таким образом, чтобы подвести итог, мне нужно объявлять и вызывать событие в DropboxFileplanViewModel.cs и получить доступ к этому событию в MainPage.xaml, поэтому я могу обработать его в MainPageViewModel.cs. Я знаю, как связать событие в MainPage.xaml с командой в MainPageViewModel, мне просто нужно знать, как сделать первый бит, т. Е. Объявить и поднять событие в DropboxFileplanViewModel.cs и получить доступ к нему в MainPage.xaml.

+0

Вы используете базовый класс для хранения ваших моделей, например ViewModelLocator (mvvm light)? Можете ли вы добавить код для своих моделей xaml и viewmodels? – SWilko

+0

Иерархия наследования моих моделей просмотра - 'ViewModel: BindableBase: INotifyPropertyChanged'. Я попытался сохранить свой первоначальный вопрос как можно более общим в отношении имен классов, но я отредактирую сообщение с фактическими фрагментами кода. –

+0

Спасибо за код. Легкий способ сделать это - использовать класс Messenger в mvvmlight, но я подумал, что сначала должен проверить, прежде чем отвечать на вопрос, поскольку вы не используете фреймворк. – SWilko

ответ

3

В XAML:

<Image Loaded="ImageLoaded" ... /> 

В xaml.cs:

public MainPageViewModel ViewModel 
{ 
    get 
    { 
     return this.DataContext as MainPageViewModel; 
    } 
} 

public void ImageLoaded(object sender, RoutedEventArgs args) 
{ 
    // call down to your view model 
    if(ViewModel != null) 
    { 
     ViewModel.ImageLoadedHandler(); 
    } 
} 

В ответ на ваш комментарий, идея та же для пользовательских UserControl. У меня есть (что я думаю) и интересное решение, которое я не часто вижу, как другие реализуют. Идея состоит в том, что каждый ViewModel имеет связанный View (я называю его владельцем) и логическим родителем. Подобно конструкциям визуального дерева XAML/WinRT, которые позволяют обходить элементы пользовательского интерфейса, отношения родителя/владельца, которые мы можем создать в наших моделях ViewModels, позволяют использовать тот же стиль обхода в нашем внутреннем коде. Рассмотрим следующее:

Предположим, у нас есть обычай UserControl, называемый MyUserControl, который находится в пространстве имен MyProject.

В MainPageView.xaml:

<Page xmlns:local="MyProject"> // or whatever your fancy-pants namespace scheme is 
    <Grid> 
     <local:MyUserControl DataContext="{Binding InnerVM}" /> 
    </Grid> 
</Page> 

В вас MainPageView.xaml.cs

public MainPageViewModel ViewModel 
{ 
    get 
    { 
     return this.DataContext as MainPageViewModel; 
    } 
} 

public MainPageView() 
{ 
    InitializeComponent(); 

    DataContext = new MainPageViewModel(null, this); 
} 

Мы добираемся туда. Теперь давайте посмотрим на MainPageViewModel.cs

public MainPageViewModel : ViewModelBase // I'll explain ViewModelBase momentarily 
{ 
    public MyUserControlViewModel InnerVM { get; set; } // should be a notifying property 

    public MainPageViewModel(ViewModelBase parent, FrameworkElement owner) 
     : base(parent, owner) 
    { 
    } 
} 

Для всех намерений и целей, MyUserControlViewModel.cs то же самое.

Вот ViewModelBase.cs (с некоторыми сокращениями):

public ViewModelBase 
{ 
    public ViewModelBase Parent { get; set; } // should be a notifying property 
    public FrameworkElement Owner { get; set; } // should be a notifying property 

    public ViewModelBase(ViewModelBase parent, FrameworkElement owner) 
    { 
     Parent = parent; 
     Owner = owner; 
    } 
} 

просто! Правильно? Теперь, что это на самом деле для нас? Посмотрим. Рассмотрим следующие:

В MyUserControl.xaml:

<Image Loaded="ImageLoaded" ... /> 

В MyUserControl.xaml.cs:

public MyUserControlVieWModel ViewModel 
{ 
    get 
    { 
     return this.DataContext as MyUserControlVieWModel; 
    } 
} 

public void ImageLoaded(object sender, RoutedEventArgs args) 
{ 
    // call down to your view model 
    if(ViewModel != null) 
    { 
     ViewModel.ImageLoadedHandler(); 
    } 
} 

MyUserControlViewModel.cs

Теперь у вас есть два варианта здесь (и я просто понял, что я могу слишком подробно объяснить вам эту проблему, и я извиняюсь. Пожалуйста, рассмотрите вариант 1 для вашего вопроса):

1- Использовать события!

public event EventHandler ImageLoaded = delegate { }; 

public void OnImageLoaded() 
{ 
    ImageLoaded(); 
} 

Тогда в MainPageViewModel.cs

public void OnImageLoaded() 
{ 
    // handle the image loading 
} 

И теперь, возможно, положить это в конструкторе:

... 
InnerVM.ImageLoaded += OnImageLoaded; 
... 

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

Второй вариант требует больше объяснений, и я должен бежать пока. Но, надеюсь, вам это удастся. Извините за короткий финал. Пожалуйста, ответьте на вопросы, если вам нужно. Удачи!

+0

Спасибо, William, который дает мне код для обработки события в 'MainPageViewModel.cs'. Возможно, я был неясно в своем вопросе, но мне нужно поднять это событие из модели представления пользовательского элемента управления «MyUserControlViewModel.cs». Пользовательский элемент управления 'MyUserControl.xaml' встроен в' MainPage.xaml'. Не могли бы вы предоставить фрагмент кода, показывающий, как это будет достигнуто? –

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