The power of WPF over WinForms is in a number of areas, one of those is
Data Binding[
^]. Once you master databinding you won't want to work directly with controls as it is far far simpler.
So with that in mind, Below is a solution that demonstrates how to work with the data.
WPF Data binding uses a notification system for when data changes. For individual objects/classes, is defind with the
INotifyPropertyChanged
interface. To simplify the implementation, I use the following base class:
public abstract class ObservableBase : INotifyPropertyChanged
{
public void Set<TValue>(ref TValue field, TValue newValue,
[CallerMemberName] string propertyName = "")
{
if (EqualityComparer<TValue>.Default
.Equals(field, default(TValue)) ||
!field.Equals(newValue))
{
field = newValue;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
1. First we need to model the data:
public class UserMenuMapping : ObservableBase
{
private long userId;
public long UserId
{
get => userId;
set => Set(ref userId, value);
}
private int menuId;
public int MenuId
{
get => menuId;
set => Set(ref menuId, value);
}
private bool canEdit;
public bool CanEdit
{
get => canEdit;
set => Set(ref canEdit, value);
}
private bool canView;
public bool CanView
{
get => canView;
set => Set(ref canView, value);
}
public int Action()
{
if (!CanEdit && CanView) return 2;
else if (CanEdit && CanView) return 3;
else if (CanEdit && !CanView) return 1;
return 0;
}
}
2. Now we need to set up the data in our code behind.
For Collections of objects, WPF has a special List collection class called
ObservableCollection
which implements the
INotifyCollectionChanged
&
INotifyPropertyChanged
interfaces for WPF data binding.
public MainWindow()
{
InitializeComponent();
DataContext = this;
MockData();
}
private void MockData()
{
Data.Clear();
for (int i = 0; i < 10; i++)
{
Data.Add(new UserMenuMapping
{
UserId = 10000 + i,
MenuId = 10+i,
CanEdit = true,
CanView = true
});
}
}
public ObservableCollection<UserMenuMapping> Data { get; }
= new ObservableCollection<UserMenuMapping>();
private void Button_Click(object sender, RoutedEventArgs e)
{
foreach (var row in Data)
{
Debug.WriteLine($"User:{row.UserId} | Menu:{row.MenuId} | Action: {row.Action()}");
}
}
}
Above we bind the XAML to the code behind. This will expose the
Data
collection
property to the UI. Now we can bind the
DataGrid
to allow the data to be edited:
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DataGrid ItemsSource="{Binding Data}"
GridLinesVisibility="None"
AlternatingRowBackground="GhostWhite" AlternationCount="1"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
AutoGenerateColumns="False"
RowDetailsVisibilityMode="VisibleWhenSelected"
VirtualizingPanel.ScrollUnit="Pixel">
<DataGrid.Columns>
<DataGridTextColumn Header="User ID" Binding="{Binding UserId}"/>
<DataGridTextColumn Header="Menu ID" Binding="{Binding MenuId}"/>
<DataGridCheckBoxColumn Header="Can Edit" Binding="{Binding CanEdit}"/>
<DataGridCheckBoxColumn Header="Can View" Binding="{Binding CanView}"/>
</DataGrid.Columns>
</DataGrid>
<Button Content="SAVE" Grid.Row="1"
Click="Button_Click"
HorizontalAlignment="Center"
Padding="10 5" Margin="10"/>
</Grid>
As you can see in the
Button_Click
you don't have to do anything fancy to read back any of the changes.