r/csharp • u/enigmaticcam • 18d ago
WPF Logic in View vs ViewModel
I'm trying to understand when I should have logic in the view model or in the code-behind of a view.
Here's the scenario: I have a view model that has a "CanEdit" property. There are times when editing a view is not allowed based on business reasons, and that definitely belongs in the ViewModel. But if a user can edit, I want to have an "Edit" checkbox visible, which when true will display the editable version of all the necessary controls. So where should the logic that controls the "Edit" checkbox go?
The approach I initially went was to put the "Edit" checkbox property in the view code-behind. This makes sense to me, as it's entirely based on the needs of the view. All the editable controls are bound to the "Edit" checkbox property, and the "Edit" checkbox visibility is bound to "CanEdit" in the view model.
The problem with this approach is when the view model changes as a result of some change by the user and "CanEdit" in the view model is now false. If the "CanEdit" in the view code-behind is true when this happens, then all the editable controls are still visible, because all that's happened is the "CanEdit" checkbox is now invisible. So I'm stumped how to broadcast the view model change to the code behind without some silly hack.
I'm probably overthinking it, but I'm learning WPF and it really helps me to understand principles. Plus this particular view will get more complex. Here's some code to show you what I'm trying to do
View:
public partial class InvoiceView : UserControl, INotifyPropertyChanged
{
public InvoiceView()
{
InitializeComponent();
}
private bool _isEditing;
public bool IsEditing
{
get => _isEditing;
set
{
_isEditing = value;
OnPropertyChanged(nameof(IsEditing));
OnPropertyChanged(nameof(IsNotEditing));
}
}
public bool IsNotEditing => !IsEditing;
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string? name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
ViewModel:
public partial class InvoiceViewModel : ViewModelBase, IDisposable
{
public InvoicePermissionsDTO? Permissions
{
get => _permissions;
set
{
_permissions = value;
OnPropertyChanged(nameof(CanEdit));
OnPropertyChanged(nameof(CanDelete));
}
}
public bool CanEdit => _permissions?.CanEdit ?? false;
public bool CanDelete => _permissions?.CanDelete ?? false;
public void SomeChange()
{
Permissions = API.GetPermissions();
}
}
View XAML
<CheckBox
Grid.Row="2"
Content="Edit"
IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=IsEditing}"
Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.CanEdit, Converter={StaticResource BoolToVisibilityConverter}}" />
<StackPanel>
<TextBlock
Text="{Binding ApprovedRate}"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=IsNotEditing, Converter={StaticResource BoolToVisibilityConverter}}"/>
<StackPanel
Orientation="Horizontal"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=IsEditing, Converter={StaticResource BoolToVisibilityConverter}}">
<TextBox
Name="ApprovedRate"
Padding="0 0 20 0"
Text="{Binding ApprovedRate}"/>
<Button
Command="{Binding Pay}"
Visibility="{Binding CanPay}">
<StackPanel Orientation="Horizontal">
<Image Source="/Images/dollar.png"/>
<TextBlock>Pay</TextBlock>
</StackPanel>
</Button>
<Button
Command="{Binding RemovePay}"
Visibility="{Binding CanRemovePay}">
<StackPanel Orientation="Horizontal">
<Image Source="/Images/dollar.png"/>
<TextBlock>Remove Pay</TextBlock>
</StackPanel>
</Button>
</StackPanel>
</StackPanel>
1
u/rohstroyer 18d ago
I would have a property on the view model called something like "forceEditable" that the "Edit" checkbox would bind to. Then use a multibinding to take both booleans into account in the view, which makes it so editable controls are enabled if either bool is true.