Easy question (I think)

Jul 2, 2013 at 4:23 AM
Markus,
In my listview xaml, I have something like:

<ListBox ItemsSource="{Binding Customers}"
SelectedItem="{Binding SelectedCustomer}"
Controls:ListBoxEx.Command="{Binding EditCustomer}"
Style="{DynamicResource myListStyle}" />

I believe that the Command binding to EditCustomer is because the style includes a button for that purpose.

I want to have a userRoles option to determine if the button is visible or not as well as a canExecute option to determine if the user has rights to that specific record.

For example, a payroll clerk may be able to see the payroll menu option, the list of employees, etc. But while they can edit and view most employees, they are restricted from doing this with VP level and above. So I can use a userRoll of "payroll" to prevent no payroll folks from seeing the payroll options. And a CanExecute that checks if the user is able to view VP level data.

Given the above use of the ListBox, above, where would I specify the UserRoles and CanExecute references to control if the user can add/edit/delete the data shown in the listbox?

I have no trouble when adding ViewActions, etc. Just trying to figure out the Listbox solution.

Thanks,

Fletcher
Coordinator
Jul 2, 2013 at 1:22 PM
The command for EditCustomer is bound in this case so when you double-click the item (or single-tap in skins like Metro), an edit form shows up (or whatever it is the command does). So this doesn't have anything to do with a button showing up. It is simply the action for the entire item in a list. And of course, your role assignment for the action should determine whether the action can fire or not. So unless the users are int he right role, the command can't be triggered.

To have a button that is bound to a certain action, you have to create a data template (such as one with columns, or a hand-crafted template) that contains the button and then bind that button to the commend. You can use the availability property on the button to hide and show the button if you want (using a trigger, most likely). Either way, when the user role on that command is set, then the command can't be triggered unless the user is in the right role. And I believe the default in WPF in that case is to show the button disabled if it can't fire. So even if you do not hide the button, security is intact and users can't trigger that command unless they are in the right role.


Markus
Jul 3, 2013 at 6:49 PM
Markus,

I tried adding a canExecute to the ViewAction:
ViewAction newViewAction = new ViewAction("Edit",
        execute: (a, o) => Controller.Action("customer", "Edit"),
        brushResourceKey: "CODE.Framework-Icon-Edit",
        canExecute: (a, o) => this.accessRights(a, o, "Edit")
        );
But this seemed to apply to the overall list, not to the individual items in the list. So no matter which item I selected, the canExecute did not refresh - only when the list first rendered.

So I edited the Edit code as follows and this works:
private void Edit(IViewAction a, object o)
{
    if (SelectedCustomer == null || !this.accessRights("edit"))
        return;

    var sinks = new ViewResultEventSinks();
    sinks.ViewClosed += (s, e) =>
    {
        Controller.Notification("Refreshing Customer list...");
        LoadData();
    };
    Controller.Action("Customer", "Edit", new
    {
        poId = SelectedCustomer.Identifier1
    }, sinks);
}
The accessRights code looks at the current record to see if it should return true or false (although in my tests, I just had it return false.)

So my question is, shouldn't the canExecute fire when the selected item changes?

Since there is no visual cue to enable/disable, my approach will work. But I thought I would check in case I solved a symptom rather than fixed the problem.

Thanks,

Fletcher
Coordinator
Jul 8, 2013 at 12:26 AM
Is this view action part of the main view model, or is it part of each individual customer "record"? It should just be on each customer. This way, each customer has it's own action and its own can-execute state. That is by far the easiest way to go. if you define it globally on the view model, then you indeed would have to re-evaluate the action based on selection and such. That is doable but harder and not as clean. (Re-evaluation can be triggered by invalidating the can-execute state of the action... there is a method for that on the action).


Markus
Jul 8, 2013 at 6:48 PM
Markus,

It's a standard list (although I am converting it to be a list result from a search - but that's another topic.) So each customer has a set of values that I check. If the user has the correct level, then he can edit (or whatever) the record. Otherwise, they can only see the limited items shown in the search results. It only needs to be evaluated once since the only way the rights might get updated would be on a new search and therefor a new list.

The test only applies to being able to view/edit/delete the customer. When they add, the access rights are set to some default value - but even if they don't have that value, if they are the creator, they can still edit/delete.

My understanding is that the canExecute on the button should fire for the currently selected record. But if I understand correctly, you are saying that the canExecute as implemented above only determines if the user can see the list, not on an item by item test and that's why it's not working, correct?

So what would I need to change so that the canExecute is applied to the double click? Or is my approach (putting the code in the edit method) just as good?

Thanks,

Fletcher
Coordinator
Jul 12, 2013 at 1:09 AM
To make sure I don't misunderstand: So there are customer "records" (objects in an ObservableCollection, I assume) and the user may or may not be able to edit those, right?

You could do the following: Let's say you have your list of customers and your selected customer defined in the view model like this:
public ObservableCollection<Customer> Customers { get; set; }
public Customer SelectedCustomer { get; set; }
You then have a list in the view like this:
<ListBox ItemsSource="{Binding Customers}" SelectedItem="{Binding SelectedCustomer}" />
You also somewhere have the edit action, kinda like this:
Actions.Add(new ViewAction("Edit", execute: EditCustomer; canExecute: CanEditCustomer));
So now, you want that action to be available or not depending on the selected "record"/customer. So you can change your SelectedCustomer property to this:
private Customer _selectedCustomer;
public Customer SelectedCustomer 
{
    get
    {
        return _selectedCustomer;
    }
    set
    {
        _selectedCustomer = value;
        foreach (var action in Actions)
        {
            if (action.Caption == "Edit") action.InvalidateCanExecute();
        }
    }
}
So this will find the action in question (you could also store a reference to the action in a private member to make it easier to identify the action) whenever the selection changes and invalidate it's CanExecute state. This will force the CanExecute delegate (pointed to CanEditCustomer() in this example) to re-fire. So then, you can simply check whatever conditions you want in that method:
private bool CanEditCustomer(IViewAction a, object o)
{
    // Do whatever you need to evaluate whether the currently selected customer can be edited
    if (SelectedCustomer == null) return false;
    if (SelectedCustomer.Whatever...) return false;
    return true;
}
Markus