Question

I have created an attached property to set the visibility of a UIElement based on a particular enum value. This works fine. However, I need to extend this so that the visibility can be overriden based on the "status" of the sender.

How can I achieve this? I had thought that I could create another attached property which the first attached property could reference, however I need to be able to bind a value to the second attached property rather than just set to an enum value.

EDIT

Below is an example of my problem:

<Window x:Class="AttachedProperty.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:attachedProperty="clr-namespace:AttachedProperty"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <StackPanel>
        <TextBlock Text="Button should be enabled?"/>
        <CheckBox IsChecked="{Binding Path=CanClick}"/>
        <Button Content="Click Me" IsEnabled="{Binding Path=CanClick}"/>
        <Button Content="Manager Only Click" attachedProperty:SecurityBehavior.IsEnabledRole="Mgr"/>
    </StackPanel>
</Grid>

The first button's enabled property is just controlled using the checkbox. The second button's enabled property is controlled by an attachedProperty that determines if you are in the correct security group:

    public class SecurityBehavior
{
    public static readonly DependencyProperty IsEnabledRoleProperty = DependencyProperty.RegisterAttached(
        "IsEnabledRole", typeof (string), typeof (SecurityBehavior), new UIPropertyMetadata(OnIsEnabledRoleChanged));

    private static void OnIsEnabledRoleChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        // In here have some logic that determines if the current user is authorised
        // Principal.IsInRole(e.NewValue.ToString() ? true : false;
        sender.SetValue(UIElement.IsEnabledProperty, true);
    }

    public static void SetIsEnabledRole(DependencyObject element, string value)
    {
        element.SetValue(IsEnabledRoleProperty, value);
    }

    public static string GetIsEnabledRole(DependencyObject element)
    {
        return (string) element.GetValue(IsEnabledRoleProperty);
    }
}

When run both buttons are enabled - the 1st because the checkbox is checked and the second because I am a manager. When I uncheck the checkbox the 1st button is disabled and I want my attached property to be able to only enable if in the correct security group AND the checkbox is checked.

How can I change by sample so that I can get have the behavior that sets the IsEnabled based on 2 possible inputs?

Was it helpful?

Solution

Not sure why you're after attached properties for this. Based on your requirement you could pull this off with a simple IValueConverter and a Binding for the Visibility of the final control.

So say we have an enum:

public enum MyEnum {
  StateOne,
  StateTwo
}

and a CheckBox such as:

<CheckBox x:Name="chkBox"
          Content="Check Me!!!" />

Now if we want some Button's Visibility to only be visible when enum is StateOne and the checkbox is checked,

we could just have a converter such as:

public class MyConverter : IValueConverter {
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
    var checkBoxIsChecked = (bool)value;
    var givenEnum = (MyEnum)parameter;
    return checkBoxIsChecked && givenEnum == MyEnum.StateOne ? Visibility.Visible : Visibility.Collapsed;
  }

  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
    throw new NotImplementedException();
  }
}

and in xaml for the Button's:

<StackPanel>
  <StackPanel.Resources>
    <local:MyConverter x:Key="MyConverter" />
  </StackPanel.Resources>
  <CheckBox x:Name="chkBox"
            Content="Check Me!!!" />

  <Button Content="Button One"
          Visibility="{Binding ElementName=chkBox,
                                Path=IsChecked,
                                Converter={StaticResource MyConverter},
                                ConverterParameter={x:Static local:MyEnum.StateOne}}" />

  <Button Content="Button Two"
          Visibility="{Binding ElementName=chkBox,
                                Path=IsChecked,
                                Converter={StaticResource MyConverter},
                                ConverterParameter={x:Static local:MyEnum.StateTwo}}" />

</StackPanel>

With this, "Button One" will become visible when the checkbox is checked, however button two will not as the ConverterParameter passed in for button two is StateTwo.

If it's the IsEnabled state you want to control of the Button, just switch the binding to that property and in the converter just return true or false accordingly instead of Visibility.Visible

Even if you choose to provide the enum value not static and dynamic, you could just make the Binding a MultiBinding and switch the IValueConverter to an IMultiValueConverter

Update:

If for whatever reason you have to go down the route of attached properties, then in the property changed callback of each attached property get the other properties value from the sender and perform your logic accordingly.

public class SecurityBehavior {
  public static readonly DependencyProperty IsEnabledRoleProperty = DependencyProperty.RegisterAttached(
    "IsEnabledRole",
    typeof(string),
    typeof(SecurityBehavior),
    new UIPropertyMetadata(OnIsEnabledRoleChanged));

  public static readonly DependencyProperty IsEnabled2RoleProperty = DependencyProperty.RegisterAttached(
    "IsEnabled2Role",
    typeof(bool),
    typeof(SecurityBehavior),
    new UIPropertyMetadata(OnIsEnabled2RoleChanged));

  private static void OnIsEnabledRoleChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) {
    HandleAttachedPropertyUpdate(sender, (string)e.NewValue, GetIsEnabled2Role(sender));
  }

  private static void OnIsEnabled2RoleChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) {
    HandleAttachedPropertyUpdate(sender, GetIsEnabledRole(sender), (bool)e.NewValue);
  }

  private static void HandleAttachedPropertyUpdate(DependencyObject sender, string isEnabledRole, bool isEnabled2Role) {
    sender.SetValue(UIElement.IsEnabledProperty, isEnabledRole == "Mgr" && isEnabled2Role);
  }

  public static void SetIsEnabledRole(DependencyObject element, string value) {
    element.SetValue(IsEnabledRoleProperty, value);
  }

  public static string GetIsEnabledRole(DependencyObject element) {
    return (string)element.GetValue(IsEnabledRoleProperty);
  }

  public static void SetIsEnabled2Role(DependencyObject element, bool value) {
    element.SetValue(IsEnabled2RoleProperty, value);
  }

  public static bool GetIsEnabled2Role(DependencyObject element) {
    return (bool)element.GetValue(IsEnabled2RoleProperty);
  }
}

and xaml:

<StackPanel>
  <CheckBox x:Name="chkBox"
            Content="Check Me!!!" />
  <Button Content="Button One"
          local:SecurityBehavior.IsEnabled2Role="{Binding ElementName=chkBox,
                                                          Path=IsChecked}"
          local:SecurityBehavior.IsEnabledRole="Mgr" />
  <Button Content="Button Two"
          local:SecurityBehavior.IsEnabled2Role="{Binding ElementName=chkBox,
                                                          Path=IsChecked}"
          local:SecurityBehavior.IsEnabledRole="NotMgr" />
</StackPanel>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top