簡體   English   中英

將 DataTemplate 中的 WPF RelayCommand 綁定到 UserControl 中的按鈕

[英]Bind a WPF RelayCommand from DataTemplate to a Button inside a UserControl

我搜索了很長時間以使用RelayCommands解決此問題,但找不到類似的解決方案。

問題是我有一個UserControl ,在這個UserConrol中是一個按鈕(代碼末尾的btcNotKnown ):

<UserControl x:Class        = "Vokabelizer.Views.viewLearnSpeak"
         xmlns          = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x        = "http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc       = "http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d        = "http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local    = "clr-namespace:Vokabelizer.Views"
         xmlns:conv     = "clr-namespace:Vokabelizer.Global.Converter"
         xmlns:ccont    = "clr-namespace:Vokabelizer.Controls;assembly=Vokabelizer.Controls"
         mc:Ignorable   = "d" 
         d:DesignHeight = "300"
         d:DesignWidth  = "800"
         Height         = "300"
         x:Name         = "root">

<UserControl.Resources>
    ...
</UserControl.Resources>

<Grid Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="0">

    <Grid.RowDefinitions>
        ...
    </Grid.RowDefinitions>

    <!-- The question to answer -->
    <Border Grid.Column         = "0"
            Grid.Row            = "0"
            Padding             = "5"
            HorizontalAlignment = "Stretch"
            VerticalAlignment   = "Stretch">

        <TextBlock Text                 = "{Binding Path=LessonNative}"
                   Style                = "{StaticResource UIText}"/>

    </Border>

    <!-- Validation content -->
    <Border Grid.Column         = "0" 
            Grid.Row            = "1"
            Padding             = "5">

        <ToggleButton x:Name        = "QnAToggle"
                      Command       = "{Binding Path=cmdValidateOK}"
                      IsThreeState  = "False">

            <ToggleButton.Style>
                <Style TargetType="ToggleButton" BasedOn="{StaticResource ValidateToggle}">
                    <Style.Triggers>
                        <Trigger Property="IsChecked" Value="False">
                            <Setter Property="Content">
                                <Setter.Value>
                                    <TextBlock Text     = "?"
                                               Style    = "{StaticResource UIText}" />
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                        <Trigger Property="IsChecked" Value="True">
                            <Setter Property="Content">
                                <Setter.Value>
                                    <TextBlock Text     = "{Binding Path=LessonForeign}"
                                               Style    = "{StaticResource UIText}" />
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </ToggleButton.Style>

        </ToggleButton>

    </Border>

    <!-- Result Evaluation buttons -->
    <Border Grid.Column         = "0"
            Grid.Row            = "2">

        <Grid>

            <Grid.ColumnDefinitions>
                ...
            </Grid.ColumnDefinitions>

            <ccont:vokButton x:Name         = "btcNotKnown"
                             Command        = "{Binding Command, ElementName=root}"
                             Grid.Column    = "0"
                             Grid.Row       = "0"
                             Content        = "Not Known"
                             Corner         = "5"
                             Style          = "{StaticResource ValidateNotKnown}"
                             Visibility     = "{Binding ElementName=QnAToggle, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=hidden}" />

        </Grid>

    </Border>

</Grid>

UserControl后面的代碼中,我定義了一個DependencyProperty來公開來自UserControl的命令,因此綁定到UserControls xaml 中的按鈕:

public partial class viewLearnSpeak : UserControl
{
    public viewLearnSpeak()
    {
        InitializeComponent();
    }

    #region DepProp: Command

    public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(viewLearnSpeak), new PropertyMetadata(null));

    public ICommand Command
    {
        get => (ICommand)GetValue(CommandProperty);
        set => SetValue(CommandProperty, value);
    }

    #endregion
}

此 Usercontrol 現在在 DataTemplate 內的 Window 中使用,這就是問題開始的地方:

<DataTemplate DataType="{x:Type vm:vmLearnSpeak}">
    <local:viewLearnSpeak Command="{Binding cmdVMNotKnown, ElementName=root}" />
</DataTemplate>

Window(View)綁定到 ViewModel(vmSession),它應該承載命令代碼,因此對於DataTemplate使用的任何 CustomControl,有效的Customcontrol可以將其命令綁定到 vmSession ViewModel 中的操作(所有當點擊它們托管的特定按鈕時, CustomControls將執行相同的操作)。

后面代碼中的命令定義:

#region Command: Not Known

private ICommand _cmdVMNotKnown;
public ICommand cmdVMNotKnown
{
    get
    {
        if (_cmdVMNotKnown == null)
        {
            _cmdVMNotKnown = new RelayCommand(
                param => this.doVMNotKnown(),
                param => { return true; }
            );
        }
        return _cmdVMNotKnown;
    }
}

protected void doVMNotKnown()
{
}

#endregion

不幸的是,我只能通過這種方式使綁定工作,而關於如何將按鈕命令綁定到不綁定到 UserControl 后面的 Viewmodel,而是綁定到以 MVVM 方式托管 Usercontrols ViewModel 的 Viewmodel 而無需依靠委托或其他處理程序...

這是完整的 window XAML(使用 DataTemplate 的 ContentControl 在最后):

<Window x:Class         = "Vokabelizer.Views.wndSession"
    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:Vokabelizer.Views"
    xmlns:res       = "clr-namespace:Vokabelizer.Resources"
    xmlns:vm        = "clr-namespace:Vokabelizer.ViewModels"
    mc:Ignorable    = "d"
    Title           = "{x:Static res:Strings.wndLearnTitle}" 
    Height          = "410"
    Width           = "800"
    ResizeMode      = "NoResize"
    x:Name          = "root">

<Window.DataContext>
    <vm:vmSession />
</Window.DataContext>

<Window.Resources>
    <DataTemplate DataType="{x:Type vm:vmLearnSpeak}">
        <local:viewLearnSpeak Command="{Binding cmdVMNotKnown, ElementName=root}" />
    </DataTemplate>
    <DataTemplate DataType="{x:Type vm:vmLearnWrite}">
        <local:viewLearnWrite />
    </DataTemplate>
    <DataTemplate DataType="{x:Type vm:vmLearnListen}">
        <local:viewLearnListen />
    </DataTemplate>
</Window.Resources>

<Grid Margin="10">

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width = "Auto" />
        <ColumnDefinition Width = "*" />
        <ColumnDefinition Width = "Auto" />
    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>
        <RowDefinition Height = "50"  />
        <RowDefinition Height = "300" />
    </Grid.RowDefinitions>

    <!-- Title description -->
    <TextBlock x:Name="txtModeDescription"  Text="{Binding LearnTitle}"
               HorizontalAlignment="Left"   VerticalAlignment="Center"
               Grid.Column="0"              Grid.ColumnSpan="1" 
               Grid.Row="0"                 Grid.RowSpan="1"
               Margin="0, 0, 30, 0"        Foreground="DarkGray"
               FontWeight="Bold" FontSize="18" FontFamily="Arial Black" />

    <!-- Progress Slider -->
    <Slider x:Name="sliderProgress"
            HorizontalAlignment="Stretch"   VerticalAlignment="Center"
            Grid.Column="1"                 Grid.ColumnSpan="1" 
            Grid.Row="0"                    Grid.RowSpan="1"
            Minimum="0"                     Maximum="11" />

    <!-- Title status -->
    <TextBlock x:Name="txtBatchAdvancement" Text="{Binding LearnProgressBatch}"
               HorizontalAlignment  = "Right"   VerticalAlignment   = "Center"
               Grid.Column          = "3"       Grid.ColumnSpan     = "1" 
               Grid.Row             = "0"       Grid.RowSpan        = "1"
               Margin               = "10, 0, 0, 0"/>

    <Border Grid.Row            = "1"
            Grid.Column         = "0"
            HorizontalAlignment = "Center"
            VerticalAlignment   = "Center"
            Padding             = "0">

        <Image Source               = "Demo.png"
               Height               = "300"
               Width                = "300"
               HorizontalAlignment  = "Center"
               VerticalAlignment    = "Center" />

    </Border>

    <ContentControl Content     = "{Binding learningViewModel}"
                    Grid.Row    = "1"   Grid.RowSpan="1"
                    Grid.Column = "1"   Grid.ColumnSpan="2"
                    Height      = "300"
                    Margin      = "20, 0, 20, 0"/>

</Grid>

這里是 Window ViewModel 中為 DataTemplate 公開 UserControl ViewModel 的部分:

private ILearnVM _learningViewModel;
/// <summary>
/// The Learning Provider View Model
/// </summary>
public ILearnVM learningViewModel 
{
    get => this._learningViewModel;
    private set
    {
        this._learningViewModel = value;

        this.OnPropertyChanged(nameof(this.learningViewModel));
    }
}

好吧,我必須非常感謝@ΩmegaMan 和其他請求一些代碼才能工作的人。

我構建了一個小型玩具項目,它確實有助於調試問題。

我改變了什么? 實際上這很簡單,在 DataTemplate 內部,我只需要使用指向 Window 的相對源並使用 DataContext 引用到 Window ViewModel 的命令屬性(在 Window Datacontext 中引用)。

我最終改變了:

<Window.Resources>
    <DataTemplate DataType="{x:Type vm:vmLearnSpeak}">
        <local:viewLearnSpeak Command="{Binding cmdVMNotKnown, ElementName=root}" />
    </DataTemplate>
    <DataTemplate DataType="{x:Type vm:vmLearnWrite}">
        <local:viewLearnWrite />
    </DataTemplate>
    <DataTemplate DataType="{x:Type vm:vmLearnListen}">
        <local:viewLearnListen />
    </DataTemplate>
</Window.Resources>

到:

<Window.Resources>
    <DataTemplate DataType="{x:Type vm:vmLearnSpeak}">
        <local:viewLearnSpeak Command="{Binding DataContext.cmdVMNotKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}" />
    </DataTemplate>
    <DataTemplate DataType="{x:Type vm:vmLearnWrite}">
        <local:viewLearnWrite />
    </DataTemplate>
    <DataTemplate DataType="{x:Type vm:vmLearnListen}">
        <local:viewLearnListen />
    </DataTemplate>
</Window.Resources>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM