【问题标题】:C# WPF MVVM - Command not Working?C# WPF MVVM - 命令不起作用?
【发布时间】:2017-08-02 14:53:49
【问题描述】:

在对类似 WinForms 的 WPF 编码进行了几个月的试验后,我目前正在创建我的第一个 MVVM 应用程序。

这个程序是我已经制作的应用程序的 MVVM 版本,一个名为 Tornado Player 的 MP3 播放器。

目前我正在处理第一个元素“观看文件夹” - 如果您将新文件添加到该文件夹​​,则使用 FileSystemWatcher 自动将 MP3 文件添加到播放列表的文件夹。

这些文件夹都可以在ListBox 中查看,其源绑定到ObservableCollection<WatchFolder> 类型的属性。

ListBox 中的每个元素都有一个 ContextMenu 和两个 MenuItems:“打开”和“删除”。 Open 将在 explorer.exe 的新实例中打开文件夹。这就是我目前正在努力的工作。稍后我会处理Remove

这是我的 ListBox 视图:

<ListBox ItemsSource="{Binding WatchFolders}" SelectedItem="{Binding SelectedWatchFolder}" Margin="5">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <Image Source="/Tornado Player;component/Images/Folder.png" Height="20" Width="20"/>
                            <TextBlock Text="{Binding Path}" Margin="5, 0, 0, 0"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>

                <ListBox.Resources>
                    <Style TargetType="ListBoxItem">
                        <Setter Property="ContextMenu">
                            <Setter.Value>
                                <ContextMenu>
                                    <MenuItem Command="{Binding OpenWatchFolder}" Header="Open" FontWeight="Bold">
                                        <MenuItem.Icon>
                                            <Image Source="/Tornado Player;component/Images/Folder Open.png"/>
                                        </MenuItem.Icon>
                                    </MenuItem>

                                    <Separator/>

                                    <MenuItem Command="{Binding DeleteWatchFolder}" Header="Remove">
                                        <MenuItem.Icon>
                                            <Image Source="/Tornado Player;component/Images/Delete.png"/>
                                        </MenuItem.Icon>
                                    </MenuItem>
                                </ContextMenu>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </ListBox.Resources>
            </ListBox>

我的 ViewModel 如下(在构造函数中,我在C:\Documents 添加了一个WatchFolder 用于测试目的):

class ViewModelMain : ViewModelBase
{
    public ViewModelMain()
    {
        WatchFolders = new ObservableCollection<WatchFolder>();

        OpenWatchFolder = new RelayCommand(DoOpenWatchFolder);

        WatchFolders.Add(new WatchFolder() { Path = "C:\\Documents" });
    }

    //Contains all of the Watch Folders as shown in the Watch Folders tab
    public ObservableCollection<WatchFolder> WatchFolders { get; set; }

    WatchFolder selectedWatchFolder;
    public WatchFolder SelectedWatchFolder
    {
        get
        {
            return selectedWatchFolder;
        }
        set
        {
            if (selectedWatchFolder != value)
            {
                selectedWatchFolder = value;
                RaisePropertyChanged("SelectedWatchFolder");
            }
        }
    }

    //'Watch Folders' tab commands
    public RelayCommand OpenWatchFolder { get; set; }
    public RelayCommand DeleteWatchFolder { get; set; }

    void DoOpenWatchFolder(object parameter)
    {
        Process.Start(SelectedWatchFolder.Path);
    }
}

我的WatchFolder型号如下:

class WatchFolder : INotifyPropertyChanged
{
    string path;
    public string Path
    {
        get
        {
            return path;
        }
        set
        {
            if (path != value)
            {
                path = value;
                RaisePropertyChanged("Path");
            }
        }
    }

    internal void RaisePropertyChanged(string prop)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

我的RelayCommand代码如下:

public class RelayCommand : ICommand
{
    #region Fields

    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;

    #endregion // Fields

    #region Constructors

    public RelayCommand(Action<object> execute)
        : this(execute, null)
    {
    }

    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }
    #endregion // Constructors

    #region ICommand Members

    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

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

    #endregion // ICommand Members
}

我已经尝试对此进行故障排除一段时间了,但我对这种设计模式和一些关键字的理解不够深入,无法看出问题所在。我尝试了各种不同的解决方案和调试,但都没有奏效,调试器甚至无法获取执行命令的代码。

单击MenuItem Open 不会执行Process.Start(path) 的代码块。我不知道为什么这不起作用,所以我想知道是否有人知道?

【问题讨论】:

  • 您提出了问题,但从未提供实际问题或详细说明实际问题是什么?什么代码没有被执行?有什么正在执行吗?您是否偶然缩小了范围?
  • 已编辑以解决此问题

标签: c# .net wpf mvvm command


【解决方案1】:

ContextMenuDataContextWatchFolder 对象,而不是 ViewModelMain 对象。 ListBox 中每个ListBoxItem 容器的DataContextItemsSource 中的数据对象,表示ListBox 中的当前行。

您可以使用RelativeSource 绑定将ListBoxItemTag 属性绑定到父ListBox(即ViewModelMain 对象)的DataContext,然后绑定Command 属性MenuItemOpenWatchFolder 属性的 Tag 属性的 ContextMenu

这行得通:

<Style TargetType="ListBoxItem">
    <Setter Property="Tag" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ListBox}}" />
    <Setter Property="ContextMenu">
        <Setter.Value>
            <ContextMenu>
                <MenuItem Command="{Binding PlacementTarget.Tag.OpenWatchFolder,
                                    RelativeSource={RelativeSource AncestorType=ContextMenu}}" Header="Open" FontWeight="Bold">
                    <MenuItem.Icon>
                        <Image Source="/Tornado Player;component/Images/Folder Open.png"/>
                    </MenuItem.Icon>
                </MenuItem>

                <Separator/>

                <MenuItem Command="{Binding PlacementTarget.Tag.DeleteWatchFolder, 
                                    RelativeSource={RelativeSource AncestorType=ContextMenu}}" Header="Remove">
                    <MenuItem.Icon>
                        <Image Source="/Tornado Player;component/Images/Delete.png"/>
                    </MenuItem.Icon>
                </MenuItem>
            </ContextMenu>
        </Setter.Value>
    </Setter>
</Style>

【讨论】:

    猜你喜欢
    • 2020-05-31
    • 2019-05-20
    • 1970-01-01
    • 2014-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多