将WPF命令绑定到C#XAML

时间:2017-06-28 14:38:37

标签: c# wpf xaml mvvm data-binding

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

XAML

TextBox

C#

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

在XAML中我希望有这样的东西:

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();
    }
}

1 个答案:

答案 0 :(得分:1)

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

“小视图模型”。我添加了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();
        }
    }
}

用于演示目的的quickie主视图模型。我们将说明您可以展示这些内容的两种不同方式:作为命名属性,或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>

我的意思是省略LabelGroupBox

<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>