【问题标题】:Bind WPF Command to ViewModel property in C# XAML在 C# XAML 中将 WPF 命令绑定到 ViewModel 属性
【发布时间】:2017-06-28 14:38:37
【问题描述】:

我正在尝试构建一个通用的Command,它可以从我的ViewModel 访问属性。在我的窗口上有几个TextBoxes,每个TextBox旁边都有一个Button。单击Button 时,我显示OpenFileDialog 并将TextBox 的Text 设置为选定的文件路径。 TextBox本身具有Binding 到ViewModel 中的属性。目前这是通过ViewModel 中的Command 实现的。 Buttons 都调用相同的Command,但每个Button 都有一个属性CommandParameter 设置为TextBox,它将接收文件路径。这样做的缺点是,我需要将参数从Commandexecution 转换为TextBox,然后设置它的Textproperty。我现在的问题是,如果我不能以某种方式将我的“命令”绑定到 TextBox 绑定到的同一属性。这是我目前所做的:

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 中我想要这样的东西:

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

【问题讨论】:

  • 你能不能不用你贴的XAML,然后就做o = ofd.FileName ?
  • 你想要的是一个小的视图模型,叫它SettingsPathSelectorViewModel,它有一个Path 属性和一个OpenFile 命令。然后,您将编写一个看起来像您最后一个 XAML sn-p 的 DataTemplate 来显示它。然后你可以让你的主视图模型暴露几个SettingsPathSelectorViewModels
  • @Milney 不幸的是没有。 o 在这种情况下只是字符串,而不是对属性的引用。
  • @EdPlunkett 我不确定我是否理解正确。如何显示控件?你能提供一个小样本吗?
  • @RomanoZumbé 努力工作

标签: c# wpf xaml mvvm data-binding


【解决方案1】:

这就是我在 cmets 中所说的:

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

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 中显示的不同大小的集合。

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 的意思:

<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(和 MVVM)新手,对各种可能性感到惊讶!非常感谢!
  • @RomanoZumbé 一旦你逐步提高学习曲线,它就会变得非常有趣。
猜你喜欢
  • 2011-09-28
  • 2012-02-23
  • 1970-01-01
  • 2011-02-01
  • 1970-01-01
  • 2021-05-12
  • 1970-01-01
  • 2017-01-18
  • 2011-03-01
相关资源
最近更新 更多