3
votes

I’m developing a WPF application in MVVM Patern. The application has a command bar and buttons for Save and Delete records.

enter image description here

The application also has a Master Detail form. It’s a User control and a DataGrid.

  • Master block : Customer Order

  • Detail block: Customer Order Lines

    (one to many relationship).

Problem:

When clicking a button in command bar, different actions need to be performed depending on the focused item.

For an example if I click the Delete button

  1. It should delete the records only in the DataGrid row, when DataGrid has focus and row(s) selected. E.g. DeleteRows() Method should be called.

  2. It should delete the entire record if the master block has focus and not datagrid focused. E.g. DeleteRecord() Method should be called.

As far as I know I can achieve this using Keyboard focus and Logical focus manager. But I was unable to find out a proper solution. I should consider that, when clicking the delete button I should ignore the focus of the Delete button.

Please help me to overcome this issue with a sample code.

1
Just a thought: are you sure this will be a well designed user experience? The behavior you described seems to be not that transparent and intuitive for the user.dymanoid
I thing this is a user friendly design for data driven application. I have seen some commercial ERP applications have such functionality.Rahul
Well, the fact that this has been already implemented somewhere doesn't mean that this is a good decision. I would be very cunfused if a single Delete button in a command bar would delete data depending on where's the keyboard cursor blinking... Anyway, it's up to you.dymanoid
Actually Delete button doesn't remove the record from the UI, It will highlights the record as "to be deleted" with strikethrough text. Clicking the Delete button second time will undo the operation. When clicking the Save button the actual deletion occurs.Rahul

1 Answers

0
votes

Since you're using the MVVM pattern, I assume that your buttons in the command bar have corresponding ICommands in the view model.

You can bind your DataGrid's SelectedItem property to a view model property (of course, with a two-way binding) and make that decision according to this property value. If it is null, so there's no item currently selected in the DataGrid, and you can delete the whole record. If it is set to an instance, then a row is selected in the DataGrid, and you can delete only one row.

If you need to exactly know which was the last focused element, you can use the Keyboard.PreviewLostKeyboardFocus attached event in your code behind. Or even better, create your own Behavior with a dependency propery that you can bind to your view model.

enum LastFocusedEntityType { None, Record, Row }

class LastFocusedEntityTrackingBehavior : Behavior<UIElement>
{
    public static readonly LastFocusedEntityProperty = DependencyProperty.Register(
        "LastFocusedEntity", 
        typeof(LastFocusedEntityType), 
        typeof(LastFocusedEntityTrackingBehavior),
        LastFocusedEntityType.None);

    public LastFocusedEntityType LastFocusedEntity
    {
        get { return (LastFocusedEntityType)this.GetValue(LastFocusedEntityProperty); }
        set { this.Setvalue(LastFocusedEntityProperty, value); }
    }

    protected override void OnAttached()
    {
        Keyboard.AddPreviewLostKeyboardFocusHandler(this.AssociatedObject, this.PreviewLostKeyboardFocusHandler);
    }

    private void PreviewLostKeyboardFocusHandler(object sender, KeyboardFocusChangedEventArgs e)
    {
        if (e.OldFocus is DataGrid)
        {
            this.LastFocusedEntity = LastFocusedEntityType.Row;
        }
        else
        {
            this.LastFocusedEntity = LastFocusedEntityType.Record;
        }
    }
}

Then you can apply this behavior to your master block container:

<UserControl>
    <i:Interaction.Behaviors>
        <local:LastFocusedEntityTrackingBehavior LastFocusedEntity="{Binding LastFocusedEntity, Mode=OneWayToSource}"/>
    </i:Interaction.Behaviors>
</UserControl>

In your view model, your ICommand's Execute() method should then look at the LastFocusedEntity property value and decide what to do next.

Note: I didn't check this code whether it compiles.