I have a WPF app with a DataGrid where the user will enter filenames. I want them to either be able to type in the filenames directly by double clicking, or click a Browse button to launch an open file dialog.
Here is the 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>
and here is the code:
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;
}
My issues are:
DataContext
is not type Input
, but rather DataGrid.NewItemPlaceholder
, so I have nothing to set the filename to. I tried instead modifying the TextBlock
Text
directly, which works, but then it doesn't commit the new row, even when issuing that command manually.DataGridTextColumn
?Coming back to this after a day, and looking at EldHasp's answer (and realizing the hard part is adding a new row), the solution seems painfully obvious now:
private void BrowseButton_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
if (!ofd.ShowDialog().Value)
return;
if (!(((Button)sender).DataContext is Input i))
Inputs.Add(new Input() { Filename = ofd.FileName });
else
i.Filename = ofd.FileName;
}
When the DataContext
is not an Input
, i.e. it's a new row, you simply create a new one (setting the appropriate property) and add it to the bound collection.