将WPF命令绑定到C#XAML中的ViewModel属性 [英] Bind WPF Command to ViewModel property in C# XAML
问题描述
我正在尝试构建通用的 Command
,该命令可以从我的 ViewModel
访问属性。在我的窗口上有几个 TextBoxes
,每个 TextBox
都有一个 Button
旁边。单击按钮
时,我显示 OpenFileDialog
并设置 Text $ c $
>。当前,这是通过 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 TextBox
has 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 TextBox
itself 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 Command
but 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 Command
execution to a TextBox
and then set its Text
property. My question is now, if I can't somehow bind my 'Command' to the same property the TextBox
is 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屋!