我有一个带有 DataGrid 的 WPF 应用程序,用户将在其中输入文件名。我希望他们能够通过双击直接输入文件名,或者单击“浏览”按钮启动打开的文件对话框。
这是 XAML:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="450" Width="800">
<DataGrid Name="TheDataGrid"
ItemsSource="{Binding Inputs}"
AutoGenerateColumns="False"
CanUserAddRows="True">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Filename" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel>
<Button DockPanel.Dock="Right" Content="Browse" Click="BrowseButton_Click" />
<TextBlock Text="{Binding Filename}" />
</DockPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<DockPanel>
<Button DockPanel.Dock="Right" Content="Browse" Click="BrowseButton_Click" />
<TextBox Text="{Binding Filename}" />
</DockPanel>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
这是代码:
public partial class MainWindow : Window
{
public ObservableCollection<Input> Inputs { get; set; } = new ObservableCollection<Input>();
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
if (!ofd.ShowDialog().Value)
return;
if (((Button)sender).DataContext is Input i)
i.Filename = ofd.FileName;
else
{
// what to do here?
}
}
}
public class Input : INotifyPropertyChanged
{
private string filename;
public string Filename
{
get { return filename; }
set
{
filename = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Filename"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
我的问题是:
DataContext
不是类型Input
,而是DataGrid.NewItemPlaceholder
,所以我没有什么可设置文件名的。我尝试直接修改 TextBlock
Text
,这有效,但即使手动发出该命令,它也不会提交新行。DataGridTextColumn
?但问题 #1 是迄今为止更紧迫的问题,是的,它只发生在新行中。
也许有更简单的方法,但从多次尝试实现来看,到目前为止我只成功了这个选项:
<DataGridTemplateColumn Header="Filename" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel>
<Button DockPanel.Dock="Right" Content="Browse"
Click="BrowseButton_Click"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGridCell}}"
Tag="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/>
<TextBlock x:Name="tb" Text="{Binding Filename}" />
</DockPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<DockPanel>
<Button x:Name="btn" DockPanel.Dock="Right" Content="Browse"
Click="BrowseButton_Click"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGridCell}}"/>
<TextBox Name="tb" Text="{Binding Filename}" />
</DockPanel>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
private static object NewItemPlaceholder = typeof(DataGrid)?
.GetProperty("NewItemPlaceholder", BindingFlags.Static | BindingFlags.NonPublic)?
.GetValue(null)!;
private void BrowseButton_Click(object sender, RoutedEventArgs e)
{
Button button = (Button)sender;
FrameworkElement cp = (FrameworkElement)button.CommandParameter;
object dc = button.DataContext;
ItemsControl ic = (ItemsControl)button.Tag;
if (dc == NewItemPlaceholder)
{
DataGridCell cell = (DataGridCell)cp; ;
DataGridRow row = DataGridRow.GetRowContainingElement(cell);
int rowindex = row.GetIndex();
DataGrid.BeginEditCommand.Execute(null, cell);
DataGridRow newrow = (DataGridRow)ic.ItemContainerGenerator.ContainerFromIndex(rowindex);
dc = newrow.DataContext;
}
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == true)
{
if (dc is Input i)
{
i.Filename = ofd.FileName;
}
else
{
throw new Exception("Something went wrong...");
}
}
}