将WPF命令绑定到C#XAML中的ViewModel属性 [英] Bind WPF Command to ViewModel property in C# XAML

查看:77
本文介绍了将WPF命令绑定到C#XAML中的ViewModel属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试构建通用的 Command ,该命令可以从我的 ViewModel 访问属性。在我的窗口上有几个 TextBoxes ,每个 TextBox 都有一个 Button 旁边。单击按钮时,我显示 OpenFileDialog 并设置 Text TextBox 的c>到所选文件的路径。 TextBox 本身对 ViewModel Binding >。当前,这是通过 ViewModel 中的 Command 实现的。 按钮都调用相同的命令,但是每个按钮具有其属性 CommandParameter 设置为将接收文件路径的 TextBox 。这样做的缺点是,我需要将参数从 Command execution强制转换为 TextBox ,然后设置其文本属性。现在的问题是,如果我无法以某种方式将我的命令绑定到绑定到 TextBox 的同一属性。这是我当前正在执行的操作:

I'm trying to build a generic Command that can access properties from my ViewModel. On my Window are several TextBoxes, each TextBoxhas a Button next to it. When the Button is clicked I show an OpenFileDialog and set the Text of the TextBox to the selected files path. The TextBoxitself has a Binding to a property in the ViewModel. Currently this is implemented with a Command in the ViewModel. The Buttons all call the same Commandbut each Button has a its property CommandParameter set to the TextBox that will receive the filepath. The drawback of this is, that I need to cast the parameter from the Commandexecution to a TextBox and then set its Textproperty. My question is now, if I can't somehow bind my 'Command' to the same property the TextBoxis bound to. Here is what I currently do:

XAML

<TextBox Text="{Binding SettingsPath}" x:Name="txtSettingsPath"></TextBox>
<Button Command="{Binding OpenFile}" 
CommandParameter="{Binding ElementName=txtSettingsPath}">...</Button>

C#

public ICommand OpenFile
{
    get
    {
        bool CanExecuteOpenFileCommand()
        {
            return true;
        }

        CommandHandler GetOpenFileCommand()
        {
            return new CommandHandler((o) =>
            {
                OpenFileDialog ofd = new OpenFileDialog();
                ofd.Multiselect = false;

                if (!string.IsNullOrEmpty(SettingsPath) && File.Exists(settingsPath))
                {
                    ofd.InitialDirectory = Path.GetDirectoryName(SettingsPath);
                }

                if(ofd.ShowDialog() == true)
                {
                    if(o is TextBox txt)
                    {
                        txt.Text = ofd.FileName;
                    }
                }

            }, CanExecuteOpenFileCommand);
        }

        return GetOpenFileCommand();
    }
}

在XAML中,我想要这样的东西:

In the XAML I would like to have something like this:

<TextBox Text="{Binding SettingsPath}"></TextBox>
<Button Command="{Binding OpenFile}" 
CommandParameter="{Binding SettingsPath}">...</Button>


推荐答案

以下是我在评论中谈论的内容:

Here's what I was talking about in comments:

小视图模型。我添加了 Label 属性,因为在我的测试项目中,它们看起来都一样。这不一定是该视图模型的一部分。

The "little viewmodel". I added a Label property because in my test project, they all looked the same. That doesn't have to be part of this viewmodel.

public class SettingsPathSelectorViewModel : ViewModelBase
{
    #region Label Property
    private String _label = default(String);
    public String Label
    {
        get { return _label; }
        set
        {
            if (value != _label)
            {
                _label = value;
                OnPropertyChanged();
            }
        }
    }
    #endregion Label Property

    #region SettingsPath Property
    private String _settingsPath = null;
    public String SettingsPath
    {
        get { return _settingsPath; }
        set
        {
            if (value != _settingsPath)
            {
                _settingsPath = value;
                OnPropertyChanged();
            }
        }
    }
    #endregion SettingsPath Property


    public ICommand OpenFile
    {
        get
        {
            bool CanExecuteOpenFileCommand()
            {
                return true;
            }

            //  We're no longer using the parameter, since we now have one                 
            //  command per SettingsPath. 
            CommandHandler GetOpenFileCommand()
            {
                return new CommandHandler((o) =>
                {
                    OpenFileDialog ofd = new OpenFileDialog();
                    ofd.Multiselect = false;

                    if (!string.IsNullOrEmpty(SettingsPath) && System.IO.File.Exists(SettingsPath))
                    {
                        ofd.InitialDirectory = System.IO.Path.GetDirectoryName(SettingsPath);
                    }

                    if (ofd.ShowDialog() == true)
                    {
                        SettingsPath = ofd.FileName;
                    }
                }, o => CanExecuteOpenFileCommand());
            }

            return GetOpenFileCommand();
        }
    }
}

演示的快捷工具主视图模型目的。我们将说明两种不同的方式来公开这些内容:要么作为命名属性,要么在 ItemsControl 中显示为不同大小的集合。

A quickie main viewmodel for demo purposes. We'll illustrate two different ways you could expose these things: Either as named properties, or a collection of varying size displayed in an ItemsControl.

public class MainViewModel : ViewModelBase
{
    public SettingsPathSelectorViewModel FirstPath { get; } = new SettingsPathSelectorViewModel() { Label = "First Path" };
    public SettingsPathSelectorViewModel SecondPath { get; } = new SettingsPathSelectorViewModel() { Label = "Second Path" };

    public ObservableCollection<SettingsPathSelectorViewModel> SettingsPaths { get; } = new ObservableCollection<SettingsPathSelectorViewModel>
    {
        new SettingsPathSelectorViewModel() { Label = "First Collection Path" },
        new SettingsPathSelectorViewModel() { Label = "Second Collection Path" },
        new SettingsPathSelectorViewModel() { Label = "Third Collection Path" },
    };
}

XAML:

<Window.Resources>
    <DataTemplate DataType="{x:Type local:SettingsPathSelectorViewModel}">
        <!-- GroupBox and Label are optional -->
        <GroupBox Header="{Binding Label}">
            <StackPanel Orientation="Horizontal">
                <TextBox Text="{Binding SettingsPath}" />
                <Button 
                    Content="..." 
                    Command="{Binding OpenFile}" 
                    HorizontalAlignment="Left" 
                    MinWidth="40" 
                    Margin="4,0,0,0" 
                    />
            </StackPanel>
        </GroupBox>
    </DataTemplate>
</Window.Resources>
<Grid>
    <StackPanel Orientation="Vertical">
        <StackPanel Orientation="Horizontal">
            <ContentControl Content="{Binding FirstPath}" />
            <ContentControl Content="{Binding SecondPath}" />
        </StackPanel>
        <ItemsControl
            ItemsSource="{Binding SettingsPaths}"
            />
    </StackPanel>
</Grid>

这就是我删除 Label 的意思。 GroupBox

Here's what I mean about omitting Label and the GroupBox:

<Window.Resources>
    <DataTemplate DataType="{x:Type local:SettingsPathSelectorViewModel}">
        <StackPanel Orientation="Horizontal">
            <TextBox Text="{Binding SettingsPath}" />
            <Button 
                Content="..." 
                Command="{Binding OpenFile}" 
                HorizontalAlignment="Left" 
                MinWidth="40" 
                Margin="4,0,0,0" 
                />
        </StackPanel>
    </DataTemplate>
</Window.Resources>
<Grid>
    <StackPanel Orientation="Vertical">
        <Label>First Path</Label>
        <ContentControl Content="{Binding FirstPath}" />
        <Label>Second Path</Label>
        <ContentControl Content="{Binding SecondPath}" />
    </StackPanel>
</Grid>

这篇关于将WPF命令绑定到C#XAML中的ViewModel属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆