简体   繁体   中英

WPF Binding Border Color in Custom Button

I have an application where a number of custom buttons are dynamically generated within a WrapPanel. All works fine and I am able to assign border thickness, ImageSource, Content etc. as I generate the buttons in code. The customer now has a requirement to allow them to choose border colours for individual buttons and try as I might I cannot figure out the correct binding scenario. I'm on a steep WPF learning curve here so it may be that my initial design is somewhat off kilter.

In my Generic.XAML I have the button specified thus:

<Style TargetType="{x:Type local:LauncherButton}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type local:LauncherButton}">
                        <Border Name="LauncherButtonBorder" BorderThickness="{TemplateBinding BThickness}"
                                CornerRadius="10" Background="White" >

                            <Border.Style>
                                <Style TargetType="{x:Type Border}">
                                    <Setter Property="BorderBrush" Value="SteelBlue" />

                                    <Style.Triggers>
                                        <Trigger Property="IsMouseOver" Value="True">
                                            <Setter Property="BorderBrush" Value="PaleGoldenrod" />
                                        </Trigger>
                                    </Style.Triggers>

                                </Style>
                            </Border.Style>
                            <DockPanel LastChildFill="True" Background="White" Margin="3">
                                <TextBlock Text="{TemplateBinding Content}" HorizontalAlignment="Center"
                                           Foreground="{DynamicResource TaskButtonTextBrush}" FontWeight="Bold"
                                           Margin="5,0,0,0" VerticalAlignment="Center" FontSize="10"
                                           Background="Transparent" DockPanel.Dock="Bottom" TextWrapping="Wrap" />
                                <Image Source="{TemplateBinding ImageSource}" Stretch="Uniform" />
                            </DockPanel>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

I want to dynamically change in c# the border colours that are currently set to static SteelBlue and PaleGoldenrod.

The button class is defined thus:

public class LauncherButton : ButtonBase
    {
        static LauncherButton()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(LauncherButton), new FrameworkPropertyMetadata(typeof(LauncherButton)));
        }

        public ImageSource ImageSource
        {
            get { return (ImageSource)GetValue(ImageSourceProperty); }
            set { SetValue(ImageSourceProperty, value); }

        }

        public Thickness BThickness
        {
            get { return (Thickness) GetValue(BThicknessProperty); }
            set { SetValue(BThicknessProperty,value);}
        }



                public static readonly DependencyProperty ImageSourceProperty =
            DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(LauncherButton), new UIPropertyMetadata(null));

        public static readonly DependencyProperty BThicknessProperty =
             DependencyProperty.Register("BThickness", typeof(Thickness), typeof(LauncherButton), new UIPropertyMetadata(null));


    }

and I'm binding some of the properties to an instance of the following class:

public class CustomButton:INotifyPropertyChanged
{
    private string _type;
    private string _buttonId;
    private string _name;
    private string _image;
    private string _link;
    private string _parent;
    private List<CustomButton> _children;
    private bool _isExpanded;
    private bool _isSelected;


    public string ButtonId
    {
        get { return _buttonId; }
        set
        {
            if (value == _buttonId) return;
            _buttonId = value;
            OnPropertyChanged("ButtonId");
        }
    }

    public string Type
    {
        get { return _type; }
        set
        {
            if (value == _type) return;
            _type = value;
            OnPropertyChanged("Type");
        }
    }

    public string Name
    {
        get { return _name; }
        set
        {
            if (value == _name) return;
            _name = value;
            OnPropertyChanged("Name");
        }
    }

    public string Image
    {
        get { return _image; }
        set
        {
            if (value == _image) return;
            _image = value;
            OnPropertyChanged("Image");
        }
    }

    public string Link
    {
        get { return _link; }
        set
        {
            if (value == _link) return;
            _link = value;
            OnPropertyChanged("Link");
        }
    }

    public string Parent
    {
        get { return _parent; }
        set
        {
            if (value == _parent) return;
            _parent = value;
            OnPropertyChanged("Parent");
        }
    }

    public List<CustomButton> Children
    {
        get { return _children; }
        set
        {
            if (Equals(value, _children)) return;
            _children = value;
            OnPropertyChanged("Children");
        }
    }

    public bool IsExpanded
    {
        get { return _isExpanded; }
        set
        {
            if (value.Equals(_isExpanded)) return;
            _isExpanded = value;
            OnPropertyChanged("IsExpanded");
        }
    }

    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            if (value.Equals(_isSelected)) return;
            _isSelected = value;
            OnPropertyChanged("IsSelected");
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

Are you trying to make the two brushes used for Border.BorderBrush dynamic?

If so you can address it in a few ways.

  • Add two dependency properties to LauncherButton for say NormalBorderBrush and MouseOverBorderBrush and then set it as you wish when you use the Button . Now to get the Border to use this, within it's Style where you set SteelBlue or PaleGoldenRod , apply a RelativeSource FindAncestor binding with AncestorType as local:LauncherButton and point it to the corresponding Brush( NormalBorderBrush or MouseOverBorderBrush )

Example:

public class LauncherButton : ButtonBase {
  ...

  public static readonly DependencyProperty NormalBorderBrushProperty =
    DependencyProperty.Register("NormalBorderBrush", typeof(Brush), typeof(LauncherButton),
      new UIPropertyMetadata(Brushes.Blue));

  public static readonly DependencyProperty MouseOverBorderBrushProperty =
    DependencyProperty.Register("MouseOverBorderBrush", typeof(Brush), typeof(LauncherButton),
      new UIPropertyMetadata(Brushes.Red));

  public Brush NormalBorderBrush
  {
    get { return (Brush)GetValue(NormalBorderBrushProperty); }
    set { SetValue(NormalBorderBrushProperty, value); }
  }

  public Brush MouseOverBorderBrush
  {
    get { return (Brush)GetValue(MouseOverBorderBrushProperty); }
    set { SetValue(MouseOverBorderBrushProperty, value); }
  }
}

in xaml:

<Border.Style>
  <Style TargetType="{x:Type Border}">
    <Setter Property="BorderBrush"
            Value="{Binding RelativeSource={RelativeSource FindAncestor,
                                                            AncestorType={x:Type local:LauncherButton}},
                            Path=NormalBorderBrush}" />
    <Style.Triggers>
      <Trigger Property="IsMouseOver"
                Value="True">
        <Setter Property="BorderBrush"
                Value="{Binding RelativeSource={RelativeSource FindAncestor,
                                                                AncestorType={x:Type local:LauncherButton}},
                                Path=MouseOverBorderBrush}" />
      </Trigger>
    </Style.Triggers>
  </Style>
</Border.Style>

and usage:

<local:LauncherButton BThickness="5"
                      Content="Hellooooo"
                      MouseOverBorderBrush="Green"
                      NormalBorderBrush="Aqua" />

Sample Download - This does not contain the converter for Brush usage, that should be easy enough to implement.

  • OR You could have two brushes defined as dynamic resources and override their color's from your code when you need to.
  • OR You can use the Button's BorderBrush property which it already has and apply this to the Border with a TemplateBinding BorderBrush . Now this would mean you need to switch the BorderBrush accordingly when your IsMouseOver state change occurs.
  • OR you could even go to extents of retrieving the button's Style and getting a reference to the Border element by finding it via it's Name and then tweaking it at run-time.

Personally I'd opt for option 1. Finally use a converter or likewise in the Binding to make it MVVM friendly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM