使用MVVM在Windows应用商店中处理事件

时间:2014-01-14 20:15:14

标签: c# xaml mvvm windows-runtime windows-store-apps

我正在使用MVVM模式开发Windows应用商店应用(没有框架,只有原始MVVM)。

我有一个用户控件DropboxFileplanUserControl.xaml,它有一个关联的视图模型DropboxFileplanViewModel.csDropboxFileplanUserControl中嵌入了MainPage.xaml,而MainPage.xaml中有MainPageViewModel.cs个关联的视图模型{。}}。

我的问题是如何在DropboxFileplanViewModel.cs中定义和引发事件并在MainPageViewModel.cs中处理?假设要引发的事件称为ImageLoaded

编辑:我添加了以下特定代码段...

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

1 个答案:

答案 0 :(得分: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(我称之为所有者)和一个逻辑父。类似于允许遍历UI元素的可视树XAML / WinRT构造,我们可以在ViewModel中创建的父/所有者关系允许在我们的后端代码中使用相同的遍历方式。请考虑以下事项:

假设我们有一个名为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(带有一些abridgments):

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将能够响应。

第二种选择需要更多的探索,我现在必须运行。但希望这会让你前进。对不起,短暂的结局。如果需要,请回答问题。祝你好运!