从UserControl

时间:2018-06-07 13:35:03

标签: c# wpf mvvm

我正在为学校开发WPF应用程序(一个黑白棋游戏)。我对MVVM很陌生,所以原谅我的 noobishness

我有一个StartScreen.xaml绑定到StartViewModel代表游戏开头的屏幕,BoardScreen.xaml绑定到BoardViewModel,这是一个屏幕黑白棋。

我在ContentControl中使用MainWindow.xaml来显示屏幕。 ViewModels实现了一个抽象的Screen类,我使用导航器在它们之间切换。

在我的BoardScreen.xaml.cs中,我试图绑定BoardViewModel中的某个事件。该事件称为GameEnded,并在游戏结束时调用。我想从后面的代码(BoardScreen.xaml.cs)订阅该事件,以便在游戏结束时播放声音。像这样:

GameEnded += BoardScreen_GameEnded
...
private void BoardScreen_GameEnded(object sender, EventArgs e)
{
    PlaySound(WinSound);
}

问题在于我真的不知道如何参加那个活动。我似乎无法使用DataContextContent属性访问它。

有没有人知道如何解决这个问题?如果我需要进一步澄清,请询问。谢谢! :)

MainWindow.xaml

<Window x:Class="View.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:View"
        xmlns:vm="clr-namespace:ViewModel;assembly=ViewModel"
        mc:Ignorable="d"
        Title="Reversi"
        SizeToContent="WidthAndHeight">
    <ContentControl Content="{Binding CurrentScreen}" Margin="30" />
</Window>

App.xaml

<Application x:Class="View.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml"
             xmlns:view="clr-namespace:View"
             xmlns:vm="clr-namespace:ViewModel;assembly=ViewModel">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Skins\blue.xaml" />
            </ResourceDictionary.MergedDictionaries>

            <DataTemplate DataType="{x:Type vm:StartViewModel}">
                <view:StartScreen />
            </DataTemplate>

            <DataTemplate DataType="{x:Type vm:BoardViewModel}">
                <view:BoardScreen />
            </DataTemplate>

        </ResourceDictionary>
    </Application.Resources>
</Application>

Navigation.cs

public abstract class Screen
{
    protected readonly Navigator navigator;

    protected Screen(Navigator navigator)
    {
        this.navigator = navigator;
    }

    protected void SwitchTo(Screen screen)
    {
        this.navigator.CurrentScreen = screen;
    }
}

public class Navigator : INotifyPropertyChanged
{
    private Screen currentScreen;

    public Navigator()
    {
        this.currentScreen = new StartViewModel(this);
    }

    public Screen CurrentScreen
    {
        get
        {
            return currentScreen;
        }
        set
        {
            this.currentScreen = value;

            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentScreen)));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

public class SimpleCommand : ICommand
{
    private readonly Action action;

    public SimpleCommand(Action action)
    {
        this.action = action;
    }

    public event EventHandler CanExecuteChanged
    {
        add { }
        remove { }
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        action();
    }
}

BoardScreen.xaml

public partial class BoardScreen : UserControl
{
    public System.Windows.Media.MediaPlayer ClickSound { get; private set; }
    public System.Windows.Media.MediaPlayer WinSound { get; private set; }

    public BoardScreen()
    {
        // Initialize sounds
        ClickSound = new MediaPlayer();
        WinSound = new MediaPlayer();

        ClickSound.Open(new Uri(@"..\..\Sounds\click.mp3", UriKind.Relative));
        WinSound.Open(new Uri(@"..\..\Sounds\win.mp3", UriKind.Relative));
    }

    private void BoardScreen_GameEnded(object sender, EventArgs e)
    {
        PlaySound(WinSound);
    }

    private void PlaySound(MediaPlayer sound)
    {
        sound.Stop();
        sound.Position = TimeSpan.Zero;
        sound.Play();
    }

    internal void PlayClickSound(object sender, RoutedEventArgs e)
    {
        PlaySound(ClickSound);
    }

    internal void PlayWinSound(object sender, RoutedEventArgs e)
    {
        PlaySound(WinSound);
    }
}

更新

正如评论中提到的@Spongebrot,我能够通过在DataContextChanged事件中进行订阅来订阅该事件。类型检查是必要的,否则我会得到NullPointerException。

DataContextChanged += (o, e) => {
    if(DataContext is BoardViewModel)
    {
        var viewModel = DataContext;
        (DataContext as BoardViewModel).GameEnded += BoardScreen_GameEnded;
    }    
};

0 个答案:

没有答案