我正在尝试构建一个可以访问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();
}
}
答案 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>
我的意思是省略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>