Rounded borders for different controls (Button, TextBox, ComboBox) via Attached Property

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
6
down vote

favorite
1












Suppose you just want to set border radius for different controls:



  • Button

  • TextBox

  • ComboBox

The most frequent approach I usually find is totally override default template like in answers



  • https://stackoverflow.com/a/4779905/1548895


  • https://stackoverflow.com/a/6746271/1548895


  • https://stackoverflow.com/a/17681374/1548895


I find overriding default styles for every CornerRadius value of Button, TextBox, ComboBox very bad idea for the following reasons:



  • You'll have to carry a lot of styles in your resources and they will be polluted with a lot of default properties.


  • You create duplicate code for every property value if you need different values


  • You don't have clean, elegant code for setting border radius itself


So I've implemented Attached property for this. The most changing part of implementing was to implement this for ComboBox



public class CornerRadiusSetter

public static CornerRadius GetCornerRadius(DependencyObject obj) => (CornerRadius)obj.GetValue(CornerRadiusProperty);

public static void SetCornerRadius(DependencyObject obj, CornerRadius value) => obj.SetValue(CornerRadiusProperty, value);

public static readonly DependencyProperty CornerRadiusProperty =
DependencyProperty.RegisterAttached(nameof(Border.CornerRadius), typeof(CornerRadius),
typeof(CornerRadiusSetter), new UIPropertyMetadata(new CornerRadius(), CornerRadiusChangedCallback));

public static void CornerRadiusChangedCallback(object sender, DependencyPropertyChangedEventArgs e)

Control control = sender as Control;

if (control == null) return;

control.Loaded += Control_Loaded;


private static void Control_Loaded(object sender, EventArgs e)
control.Template == null) return;

control.ApplyTemplate();

CornerRadius cornerRadius = GetCornerRadius(control);

Control toggleButton = control.Template.FindName("toggleButton", control) as Control;

if (control is ComboBox && toggleButton != null)

toggleButton.ApplyTemplate();

// Set border radius for border radius border
Border toggleButtonBorder = toggleButton.Template.FindName("templateRoot", toggleButton) as Border;
toggleButtonBorder.CornerRadius = cornerRadius;

// Expand padding for combobox to avoid text clipping by border radius
control.Padding = new Thickness(
control.Padding.Left + cornerRadius.BottomLeft,
control.Padding.Top,
control.Padding.Right + cornerRadius.BottomRight,
control.Padding.Bottom);

// Decrease width of dropdown and center it to avoid showing "sticking" dropdown corners
Popup popup = control.Template.FindName("PART_Popup", control) as Popup;

Popup popup = control.Template.FindName("PART_Popup", control) as Popup;

if (popup != null)

double offset = cornerRadius.BottomLeft - 1;
if (offset > 0)
popup.HorizontalOffset = offset;


SystemDropShadowChrome shadowChrome = control.Template.FindName("shadow", control) as SystemDropShadowChrome;

if (shadowChrome != null)

double minWidth = control.ActualWidth - cornerRadius.BottomLeft - cornerRadius.BottomRight;
if (minWidth > 0)
shadowChrome.MinWidth = minWidth;



// setting borders for non-combobox controls
Border border = control.Template.FindName("border", control) as Border;

if (border == null) return;

border.CornerRadius = cornerRadius;




So now you can set borders either in styles of in code via single property



<Button local:CornerRadiusSetter.CornerRadius="10">Button</Button>
<Button local:CornerRadiusSetter.CornerRadius="5,7,10,12">Button</Button>
<TextBox local:CornerRadiusSetter.CornerRadius="3,0,0,3" />
<TextBox local:CornerRadiusSetter.CornerRadius="7,8,2,1" />
<ComboBox local:CornerRadiusSetter.CornerRadius="5" />
<ComboBox local:CornerRadiusSetter.CornerRadius="7" />


Or for multiple controls inside Resources:



<Style TargetType="Button">
<Setter Property="local:CornerRadiusSetter.CornerRadius" Value="10" />
</Style>



Results of styling



Styling demo







share|improve this question





















  • Do you mind adding some screenshots? :-)
    – t3chb0t
    Jun 22 at 10:39










  • @t3chb0t Not at all. You mean I should add screenshot or you will? Also I want to know what kind of screenshot do you want.
    – Vadim Ovchinnikov
    Jun 22 at 10:43






  • 1




    Oh, sorry, I meat if you have any? I'd be nice to see how the result of this utility looks like.
    – t3chb0t
    Jun 22 at 10:44






  • 1




    @t3chb0t Added screenshot. There you can see also that dropdown width is decreased to avoid dropdown corner sticking.
    – Vadim Ovchinnikov
    Jun 22 at 11:19






  • 1




    @VadimOvchinnikov you don't have to copy-paste. You can still bind CornerRadius property inside a template to some attached property (such as Border.CornerRadius). And then set that property on actual controls.
    – Nikita B
    Jun 22 at 12:15
















up vote
6
down vote

favorite
1












Suppose you just want to set border radius for different controls:



  • Button

  • TextBox

  • ComboBox

The most frequent approach I usually find is totally override default template like in answers



  • https://stackoverflow.com/a/4779905/1548895


  • https://stackoverflow.com/a/6746271/1548895


  • https://stackoverflow.com/a/17681374/1548895


I find overriding default styles for every CornerRadius value of Button, TextBox, ComboBox very bad idea for the following reasons:



  • You'll have to carry a lot of styles in your resources and they will be polluted with a lot of default properties.


  • You create duplicate code for every property value if you need different values


  • You don't have clean, elegant code for setting border radius itself


So I've implemented Attached property for this. The most changing part of implementing was to implement this for ComboBox



public class CornerRadiusSetter

public static CornerRadius GetCornerRadius(DependencyObject obj) => (CornerRadius)obj.GetValue(CornerRadiusProperty);

public static void SetCornerRadius(DependencyObject obj, CornerRadius value) => obj.SetValue(CornerRadiusProperty, value);

public static readonly DependencyProperty CornerRadiusProperty =
DependencyProperty.RegisterAttached(nameof(Border.CornerRadius), typeof(CornerRadius),
typeof(CornerRadiusSetter), new UIPropertyMetadata(new CornerRadius(), CornerRadiusChangedCallback));

public static void CornerRadiusChangedCallback(object sender, DependencyPropertyChangedEventArgs e)

Control control = sender as Control;

if (control == null) return;

control.Loaded += Control_Loaded;


private static void Control_Loaded(object sender, EventArgs e)
control.Template == null) return;

control.ApplyTemplate();

CornerRadius cornerRadius = GetCornerRadius(control);

Control toggleButton = control.Template.FindName("toggleButton", control) as Control;

if (control is ComboBox && toggleButton != null)

toggleButton.ApplyTemplate();

// Set border radius for border radius border
Border toggleButtonBorder = toggleButton.Template.FindName("templateRoot", toggleButton) as Border;
toggleButtonBorder.CornerRadius = cornerRadius;

// Expand padding for combobox to avoid text clipping by border radius
control.Padding = new Thickness(
control.Padding.Left + cornerRadius.BottomLeft,
control.Padding.Top,
control.Padding.Right + cornerRadius.BottomRight,
control.Padding.Bottom);

// Decrease width of dropdown and center it to avoid showing "sticking" dropdown corners
Popup popup = control.Template.FindName("PART_Popup", control) as Popup;

Popup popup = control.Template.FindName("PART_Popup", control) as Popup;

if (popup != null)

double offset = cornerRadius.BottomLeft - 1;
if (offset > 0)
popup.HorizontalOffset = offset;


SystemDropShadowChrome shadowChrome = control.Template.FindName("shadow", control) as SystemDropShadowChrome;

if (shadowChrome != null)

double minWidth = control.ActualWidth - cornerRadius.BottomLeft - cornerRadius.BottomRight;
if (minWidth > 0)
shadowChrome.MinWidth = minWidth;



// setting borders for non-combobox controls
Border border = control.Template.FindName("border", control) as Border;

if (border == null) return;

border.CornerRadius = cornerRadius;




So now you can set borders either in styles of in code via single property



<Button local:CornerRadiusSetter.CornerRadius="10">Button</Button>
<Button local:CornerRadiusSetter.CornerRadius="5,7,10,12">Button</Button>
<TextBox local:CornerRadiusSetter.CornerRadius="3,0,0,3" />
<TextBox local:CornerRadiusSetter.CornerRadius="7,8,2,1" />
<ComboBox local:CornerRadiusSetter.CornerRadius="5" />
<ComboBox local:CornerRadiusSetter.CornerRadius="7" />


Or for multiple controls inside Resources:



<Style TargetType="Button">
<Setter Property="local:CornerRadiusSetter.CornerRadius" Value="10" />
</Style>



Results of styling



Styling demo







share|improve this question





















  • Do you mind adding some screenshots? :-)
    – t3chb0t
    Jun 22 at 10:39










  • @t3chb0t Not at all. You mean I should add screenshot or you will? Also I want to know what kind of screenshot do you want.
    – Vadim Ovchinnikov
    Jun 22 at 10:43






  • 1




    Oh, sorry, I meat if you have any? I'd be nice to see how the result of this utility looks like.
    – t3chb0t
    Jun 22 at 10:44






  • 1




    @t3chb0t Added screenshot. There you can see also that dropdown width is decreased to avoid dropdown corner sticking.
    – Vadim Ovchinnikov
    Jun 22 at 11:19






  • 1




    @VadimOvchinnikov you don't have to copy-paste. You can still bind CornerRadius property inside a template to some attached property (such as Border.CornerRadius). And then set that property on actual controls.
    – Nikita B
    Jun 22 at 12:15












up vote
6
down vote

favorite
1









up vote
6
down vote

favorite
1






1





Suppose you just want to set border radius for different controls:



  • Button

  • TextBox

  • ComboBox

The most frequent approach I usually find is totally override default template like in answers



  • https://stackoverflow.com/a/4779905/1548895


  • https://stackoverflow.com/a/6746271/1548895


  • https://stackoverflow.com/a/17681374/1548895


I find overriding default styles for every CornerRadius value of Button, TextBox, ComboBox very bad idea for the following reasons:



  • You'll have to carry a lot of styles in your resources and they will be polluted with a lot of default properties.


  • You create duplicate code for every property value if you need different values


  • You don't have clean, elegant code for setting border radius itself


So I've implemented Attached property for this. The most changing part of implementing was to implement this for ComboBox



public class CornerRadiusSetter

public static CornerRadius GetCornerRadius(DependencyObject obj) => (CornerRadius)obj.GetValue(CornerRadiusProperty);

public static void SetCornerRadius(DependencyObject obj, CornerRadius value) => obj.SetValue(CornerRadiusProperty, value);

public static readonly DependencyProperty CornerRadiusProperty =
DependencyProperty.RegisterAttached(nameof(Border.CornerRadius), typeof(CornerRadius),
typeof(CornerRadiusSetter), new UIPropertyMetadata(new CornerRadius(), CornerRadiusChangedCallback));

public static void CornerRadiusChangedCallback(object sender, DependencyPropertyChangedEventArgs e)

Control control = sender as Control;

if (control == null) return;

control.Loaded += Control_Loaded;


private static void Control_Loaded(object sender, EventArgs e)
control.Template == null) return;

control.ApplyTemplate();

CornerRadius cornerRadius = GetCornerRadius(control);

Control toggleButton = control.Template.FindName("toggleButton", control) as Control;

if (control is ComboBox && toggleButton != null)

toggleButton.ApplyTemplate();

// Set border radius for border radius border
Border toggleButtonBorder = toggleButton.Template.FindName("templateRoot", toggleButton) as Border;
toggleButtonBorder.CornerRadius = cornerRadius;

// Expand padding for combobox to avoid text clipping by border radius
control.Padding = new Thickness(
control.Padding.Left + cornerRadius.BottomLeft,
control.Padding.Top,
control.Padding.Right + cornerRadius.BottomRight,
control.Padding.Bottom);

// Decrease width of dropdown and center it to avoid showing "sticking" dropdown corners
Popup popup = control.Template.FindName("PART_Popup", control) as Popup;

Popup popup = control.Template.FindName("PART_Popup", control) as Popup;

if (popup != null)

double offset = cornerRadius.BottomLeft - 1;
if (offset > 0)
popup.HorizontalOffset = offset;


SystemDropShadowChrome shadowChrome = control.Template.FindName("shadow", control) as SystemDropShadowChrome;

if (shadowChrome != null)

double minWidth = control.ActualWidth - cornerRadius.BottomLeft - cornerRadius.BottomRight;
if (minWidth > 0)
shadowChrome.MinWidth = minWidth;



// setting borders for non-combobox controls
Border border = control.Template.FindName("border", control) as Border;

if (border == null) return;

border.CornerRadius = cornerRadius;




So now you can set borders either in styles of in code via single property



<Button local:CornerRadiusSetter.CornerRadius="10">Button</Button>
<Button local:CornerRadiusSetter.CornerRadius="5,7,10,12">Button</Button>
<TextBox local:CornerRadiusSetter.CornerRadius="3,0,0,3" />
<TextBox local:CornerRadiusSetter.CornerRadius="7,8,2,1" />
<ComboBox local:CornerRadiusSetter.CornerRadius="5" />
<ComboBox local:CornerRadiusSetter.CornerRadius="7" />


Or for multiple controls inside Resources:



<Style TargetType="Button">
<Setter Property="local:CornerRadiusSetter.CornerRadius" Value="10" />
</Style>



Results of styling



Styling demo







share|improve this question













Suppose you just want to set border radius for different controls:



  • Button

  • TextBox

  • ComboBox

The most frequent approach I usually find is totally override default template like in answers



  • https://stackoverflow.com/a/4779905/1548895


  • https://stackoverflow.com/a/6746271/1548895


  • https://stackoverflow.com/a/17681374/1548895


I find overriding default styles for every CornerRadius value of Button, TextBox, ComboBox very bad idea for the following reasons:



  • You'll have to carry a lot of styles in your resources and they will be polluted with a lot of default properties.


  • You create duplicate code for every property value if you need different values


  • You don't have clean, elegant code for setting border radius itself


So I've implemented Attached property for this. The most changing part of implementing was to implement this for ComboBox



public class CornerRadiusSetter

public static CornerRadius GetCornerRadius(DependencyObject obj) => (CornerRadius)obj.GetValue(CornerRadiusProperty);

public static void SetCornerRadius(DependencyObject obj, CornerRadius value) => obj.SetValue(CornerRadiusProperty, value);

public static readonly DependencyProperty CornerRadiusProperty =
DependencyProperty.RegisterAttached(nameof(Border.CornerRadius), typeof(CornerRadius),
typeof(CornerRadiusSetter), new UIPropertyMetadata(new CornerRadius(), CornerRadiusChangedCallback));

public static void CornerRadiusChangedCallback(object sender, DependencyPropertyChangedEventArgs e)

Control control = sender as Control;

if (control == null) return;

control.Loaded += Control_Loaded;


private static void Control_Loaded(object sender, EventArgs e)
control.Template == null) return;

control.ApplyTemplate();

CornerRadius cornerRadius = GetCornerRadius(control);

Control toggleButton = control.Template.FindName("toggleButton", control) as Control;

if (control is ComboBox && toggleButton != null)

toggleButton.ApplyTemplate();

// Set border radius for border radius border
Border toggleButtonBorder = toggleButton.Template.FindName("templateRoot", toggleButton) as Border;
toggleButtonBorder.CornerRadius = cornerRadius;

// Expand padding for combobox to avoid text clipping by border radius
control.Padding = new Thickness(
control.Padding.Left + cornerRadius.BottomLeft,
control.Padding.Top,
control.Padding.Right + cornerRadius.BottomRight,
control.Padding.Bottom);

// Decrease width of dropdown and center it to avoid showing "sticking" dropdown corners
Popup popup = control.Template.FindName("PART_Popup", control) as Popup;

Popup popup = control.Template.FindName("PART_Popup", control) as Popup;

if (popup != null)

double offset = cornerRadius.BottomLeft - 1;
if (offset > 0)
popup.HorizontalOffset = offset;


SystemDropShadowChrome shadowChrome = control.Template.FindName("shadow", control) as SystemDropShadowChrome;

if (shadowChrome != null)

double minWidth = control.ActualWidth - cornerRadius.BottomLeft - cornerRadius.BottomRight;
if (minWidth > 0)
shadowChrome.MinWidth = minWidth;



// setting borders for non-combobox controls
Border border = control.Template.FindName("border", control) as Border;

if (border == null) return;

border.CornerRadius = cornerRadius;




So now you can set borders either in styles of in code via single property



<Button local:CornerRadiusSetter.CornerRadius="10">Button</Button>
<Button local:CornerRadiusSetter.CornerRadius="5,7,10,12">Button</Button>
<TextBox local:CornerRadiusSetter.CornerRadius="3,0,0,3" />
<TextBox local:CornerRadiusSetter.CornerRadius="7,8,2,1" />
<ComboBox local:CornerRadiusSetter.CornerRadius="5" />
<ComboBox local:CornerRadiusSetter.CornerRadius="7" />


Or for multiple controls inside Resources:



<Style TargetType="Button">
<Setter Property="local:CornerRadiusSetter.CornerRadius" Value="10" />
</Style>



Results of styling



Styling demo









share|improve this question












share|improve this question




share|improve this question








edited Jun 22 at 14:49









paparazzo

4,8131730




4,8131730









asked Jun 22 at 8:00









Vadim Ovchinnikov

1,0101417




1,0101417











  • Do you mind adding some screenshots? :-)
    – t3chb0t
    Jun 22 at 10:39










  • @t3chb0t Not at all. You mean I should add screenshot or you will? Also I want to know what kind of screenshot do you want.
    – Vadim Ovchinnikov
    Jun 22 at 10:43






  • 1




    Oh, sorry, I meat if you have any? I'd be nice to see how the result of this utility looks like.
    – t3chb0t
    Jun 22 at 10:44






  • 1




    @t3chb0t Added screenshot. There you can see also that dropdown width is decreased to avoid dropdown corner sticking.
    – Vadim Ovchinnikov
    Jun 22 at 11:19






  • 1




    @VadimOvchinnikov you don't have to copy-paste. You can still bind CornerRadius property inside a template to some attached property (such as Border.CornerRadius). And then set that property on actual controls.
    – Nikita B
    Jun 22 at 12:15
















  • Do you mind adding some screenshots? :-)
    – t3chb0t
    Jun 22 at 10:39










  • @t3chb0t Not at all. You mean I should add screenshot or you will? Also I want to know what kind of screenshot do you want.
    – Vadim Ovchinnikov
    Jun 22 at 10:43






  • 1




    Oh, sorry, I meat if you have any? I'd be nice to see how the result of this utility looks like.
    – t3chb0t
    Jun 22 at 10:44






  • 1




    @t3chb0t Added screenshot. There you can see also that dropdown width is decreased to avoid dropdown corner sticking.
    – Vadim Ovchinnikov
    Jun 22 at 11:19






  • 1




    @VadimOvchinnikov you don't have to copy-paste. You can still bind CornerRadius property inside a template to some attached property (such as Border.CornerRadius). And then set that property on actual controls.
    – Nikita B
    Jun 22 at 12:15















Do you mind adding some screenshots? :-)
– t3chb0t
Jun 22 at 10:39




Do you mind adding some screenshots? :-)
– t3chb0t
Jun 22 at 10:39












@t3chb0t Not at all. You mean I should add screenshot or you will? Also I want to know what kind of screenshot do you want.
– Vadim Ovchinnikov
Jun 22 at 10:43




@t3chb0t Not at all. You mean I should add screenshot or you will? Also I want to know what kind of screenshot do you want.
– Vadim Ovchinnikov
Jun 22 at 10:43




1




1




Oh, sorry, I meat if you have any? I'd be nice to see how the result of this utility looks like.
– t3chb0t
Jun 22 at 10:44




Oh, sorry, I meat if you have any? I'd be nice to see how the result of this utility looks like.
– t3chb0t
Jun 22 at 10:44




1




1




@t3chb0t Added screenshot. There you can see also that dropdown width is decreased to avoid dropdown corner sticking.
– Vadim Ovchinnikov
Jun 22 at 11:19




@t3chb0t Added screenshot. There you can see also that dropdown width is decreased to avoid dropdown corner sticking.
– Vadim Ovchinnikov
Jun 22 at 11:19




1




1




@VadimOvchinnikov you don't have to copy-paste. You can still bind CornerRadius property inside a template to some attached property (such as Border.CornerRadius). And then set that property on actual controls.
– Nikita B
Jun 22 at 12:15




@VadimOvchinnikov you don't have to copy-paste. You can still bind CornerRadius property inside a template to some attached property (such as Border.CornerRadius). And then set that property on actual controls.
– Nikita B
Jun 22 at 12:15










2 Answers
2






active

oldest

votes

















up vote
7
down vote



accepted










This is a nice feature, and all in all it works well. A couple of remarks though:




Split up the main method in two methods - one for ComboBoxes and one for other controls. It is more readable and easier to maintain:



private static void Control_Loaded(object sender, EventArgs e)

Control control = sender as Control;

if (control == null



Be aware that you set the Loaded event handler every time you change the CornerRadius. You probably don't change the corner radius after app load, but if you do, you attach a new handler for the controls Loaded event. It's no big deal because it is only called once (at load time) - but anyway it looks bad. You could probably handle it like this:



public static void CornerRadiusChangedCallback(object sender, DependencyPropertyChangedEventArgs e)

Control control = sender as Control;

if (control == null) return;

control.Loaded -= Control_Loaded;
control.Loaded += Control_Loaded;




You should be aware that SystemDropShadowChrome is defined in more than one assembly (Aero and Aero2 for instance) so you'll have to link to the right assembly. Which one that is in use I think is system dependent(?). You could maybe use Reflection to overcome this(?)




Setting the properties of the Popup/Dropdown of the ComboBox at load time is a bad idea, because if the ComboBox changes size (width), the settings you made at load time are not adjusted to the new size. Instead you should make the settings for the ComboBox in both the Loaded and the SizeChanged events or maybe just in the DropDownOpened event.






share|improve this answer























  • Regarding SystemDropShadowChrome type: you are right. I can replace this line with Decorator shadowChrome = control.Template.FindName("shadow", control) as Decorator; and avoid extra DLLs dependency.
    – Vadim Ovchinnikov
    Jun 22 at 10:45











  • Regarding last point I don't know how practical it could be. I don't mind this feature, but dynamically changing size for ComboBox seems to be either bad UI experience or rare need. Usual ComboBoxes have fixed sizes. Am I missing something?
    – Vadim Ovchinnikov
    Jun 22 at 10:47










  • @VadimOvchinnikov: About SystemDropShadowChrome - Yes that's a way to go or simply just FrameworkElement :-)
    – Henrik Hansen
    Jun 22 at 10:49










  • @VadimOvchinnikov: Changing the with of a ComboBox in a dialog for instance or in a sizable docking window is very common and a very good UI experience - IMO :-)
    – Henrik Hansen
    Jun 22 at 10:52

















up vote
3
down vote














Holy crap. All that just to get Rounded corners?




Yes, I wholeheartedly agree with that comment. It is astonishing, how some things that sound trivial - are extremely complicated in WPF. But there is only so much that you can do. You can either fight it or embrace it (you can also choose different framework, but that's another story). And when choosing between those two options, you have to consider this:



What are the chances, that you are not going to change default templates anyway in the future? In my experience those chances are often quite slim. If you care enough about your UI that corner radius bothers you, then that's probably not the only thing you will need to change. What about that highlight color? What about other controls? Will you change corner radius of scrollbar buttons also? Progressbar?




Correct me if I'm wrong. For this you should 1) Store nearly a hundred
lines of code for default button styles, 2) Create new class with
attached property (you can't write Border.CornerRadius="10" for
button, can you?)




Yes, you can't, but you can set it in style. This code works:



<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Button>
<Button.Style>
<Style TargetType="Button">
<Setter Property="Border.CornerRadius" Value="15"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="100"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border CornerRadius="TemplateBinding Border.CornerRadius"
BorderThickness="1" Background="Green" BorderBrush="Black"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</Page>


But you can, of course, use your own stand-alone attached property (there is no need to subclass anything). You can even make it inherited, and set it for you entire MainWindow, so that all controls are affected.




Don't get me wrong, I often write similar behaviors myself. I just think that whenever I want to modify control template via behavior, it is a good idea to carefully consider the implications. Yes, when you override template, there is some initial copy-pasting. But then it just works. Want to databind Padding property? Fine. Want to change corner radius property via animation? Sure. Want to use those buttons inside a docked window, that can reload its content multiple times? No problem.



You solution on the other hand is very much tied to your current use case:



1) It overrides padding property, potentially breaking triggers and databindings.



2) It does not allow modifications to CornerRadius after control is loaded.



3) It will not work, if Loaded event triggers more than once. Most notably, your padding will just keep growing.



4) It relies heavily on the fact, that default button template has an element named border. But will it in the future versions of the framework? I mean realistically, it probably will, but this assumption still feels shaky.



There might be other edge cases, where you behavior will fail but the template will not. So is it all worth it? I am not so sure.






share|improve this answer























  • By the way, in this button template, are you missing default trigger implementation and if you don't include them, they won't work, am I right?
    – Vadim Ovchinnikov
    Jun 22 at 14:07










  • Regarding point #3 I suppose I would just set Padding to Border element and that will work even in case of multiple Loaded triggering. I don't understand why this won't work as desired.
    – Vadim Ovchinnikov
    Jun 22 at 14:10










  • @VadimOvchinnikov yes, it is missing pretty much everything, except for border. :) This was just an example of how you can replace hardcoded radius with TemplateBinding in your actual template.
    – Nikita B
    Jun 22 at 14:12










  • Regarding point #2 I agree but I assume this can be solved but I haven't tried because I won't be using this case.
    – Vadim Ovchinnikov
    Jun 22 at 14:13










  • Maybe it's offtopic, but I'm very curious about idea to "choose different framework". Are there any other powerful enterprise-level frameworks except WPF?
    – Vadim Ovchinnikov
    Jun 22 at 14:16










Your Answer




StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f197042%2frounded-borders-for-different-controls-button-textbox-combobox-via-attached%23new-answer', 'question_page');

);

Post as a guest






























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
7
down vote



accepted










This is a nice feature, and all in all it works well. A couple of remarks though:




Split up the main method in two methods - one for ComboBoxes and one for other controls. It is more readable and easier to maintain:



private static void Control_Loaded(object sender, EventArgs e)

Control control = sender as Control;

if (control == null



Be aware that you set the Loaded event handler every time you change the CornerRadius. You probably don't change the corner radius after app load, but if you do, you attach a new handler for the controls Loaded event. It's no big deal because it is only called once (at load time) - but anyway it looks bad. You could probably handle it like this:



public static void CornerRadiusChangedCallback(object sender, DependencyPropertyChangedEventArgs e)

Control control = sender as Control;

if (control == null) return;

control.Loaded -= Control_Loaded;
control.Loaded += Control_Loaded;




You should be aware that SystemDropShadowChrome is defined in more than one assembly (Aero and Aero2 for instance) so you'll have to link to the right assembly. Which one that is in use I think is system dependent(?). You could maybe use Reflection to overcome this(?)




Setting the properties of the Popup/Dropdown of the ComboBox at load time is a bad idea, because if the ComboBox changes size (width), the settings you made at load time are not adjusted to the new size. Instead you should make the settings for the ComboBox in both the Loaded and the SizeChanged events or maybe just in the DropDownOpened event.






share|improve this answer























  • Regarding SystemDropShadowChrome type: you are right. I can replace this line with Decorator shadowChrome = control.Template.FindName("shadow", control) as Decorator; and avoid extra DLLs dependency.
    – Vadim Ovchinnikov
    Jun 22 at 10:45











  • Regarding last point I don't know how practical it could be. I don't mind this feature, but dynamically changing size for ComboBox seems to be either bad UI experience or rare need. Usual ComboBoxes have fixed sizes. Am I missing something?
    – Vadim Ovchinnikov
    Jun 22 at 10:47










  • @VadimOvchinnikov: About SystemDropShadowChrome - Yes that's a way to go or simply just FrameworkElement :-)
    – Henrik Hansen
    Jun 22 at 10:49










  • @VadimOvchinnikov: Changing the with of a ComboBox in a dialog for instance or in a sizable docking window is very common and a very good UI experience - IMO :-)
    – Henrik Hansen
    Jun 22 at 10:52














up vote
7
down vote



accepted










This is a nice feature, and all in all it works well. A couple of remarks though:




Split up the main method in two methods - one for ComboBoxes and one for other controls. It is more readable and easier to maintain:



private static void Control_Loaded(object sender, EventArgs e)

Control control = sender as Control;

if (control == null



Be aware that you set the Loaded event handler every time you change the CornerRadius. You probably don't change the corner radius after app load, but if you do, you attach a new handler for the controls Loaded event. It's no big deal because it is only called once (at load time) - but anyway it looks bad. You could probably handle it like this:



public static void CornerRadiusChangedCallback(object sender, DependencyPropertyChangedEventArgs e)

Control control = sender as Control;

if (control == null) return;

control.Loaded -= Control_Loaded;
control.Loaded += Control_Loaded;




You should be aware that SystemDropShadowChrome is defined in more than one assembly (Aero and Aero2 for instance) so you'll have to link to the right assembly. Which one that is in use I think is system dependent(?). You could maybe use Reflection to overcome this(?)




Setting the properties of the Popup/Dropdown of the ComboBox at load time is a bad idea, because if the ComboBox changes size (width), the settings you made at load time are not adjusted to the new size. Instead you should make the settings for the ComboBox in both the Loaded and the SizeChanged events or maybe just in the DropDownOpened event.






share|improve this answer























  • Regarding SystemDropShadowChrome type: you are right. I can replace this line with Decorator shadowChrome = control.Template.FindName("shadow", control) as Decorator; and avoid extra DLLs dependency.
    – Vadim Ovchinnikov
    Jun 22 at 10:45











  • Regarding last point I don't know how practical it could be. I don't mind this feature, but dynamically changing size for ComboBox seems to be either bad UI experience or rare need. Usual ComboBoxes have fixed sizes. Am I missing something?
    – Vadim Ovchinnikov
    Jun 22 at 10:47










  • @VadimOvchinnikov: About SystemDropShadowChrome - Yes that's a way to go or simply just FrameworkElement :-)
    – Henrik Hansen
    Jun 22 at 10:49










  • @VadimOvchinnikov: Changing the with of a ComboBox in a dialog for instance or in a sizable docking window is very common and a very good UI experience - IMO :-)
    – Henrik Hansen
    Jun 22 at 10:52












up vote
7
down vote



accepted







up vote
7
down vote



accepted






This is a nice feature, and all in all it works well. A couple of remarks though:




Split up the main method in two methods - one for ComboBoxes and one for other controls. It is more readable and easier to maintain:



private static void Control_Loaded(object sender, EventArgs e)

Control control = sender as Control;

if (control == null



Be aware that you set the Loaded event handler every time you change the CornerRadius. You probably don't change the corner radius after app load, but if you do, you attach a new handler for the controls Loaded event. It's no big deal because it is only called once (at load time) - but anyway it looks bad. You could probably handle it like this:



public static void CornerRadiusChangedCallback(object sender, DependencyPropertyChangedEventArgs e)

Control control = sender as Control;

if (control == null) return;

control.Loaded -= Control_Loaded;
control.Loaded += Control_Loaded;




You should be aware that SystemDropShadowChrome is defined in more than one assembly (Aero and Aero2 for instance) so you'll have to link to the right assembly. Which one that is in use I think is system dependent(?). You could maybe use Reflection to overcome this(?)




Setting the properties of the Popup/Dropdown of the ComboBox at load time is a bad idea, because if the ComboBox changes size (width), the settings you made at load time are not adjusted to the new size. Instead you should make the settings for the ComboBox in both the Loaded and the SizeChanged events or maybe just in the DropDownOpened event.






share|improve this answer















This is a nice feature, and all in all it works well. A couple of remarks though:




Split up the main method in two methods - one for ComboBoxes and one for other controls. It is more readable and easier to maintain:



private static void Control_Loaded(object sender, EventArgs e)

Control control = sender as Control;

if (control == null



Be aware that you set the Loaded event handler every time you change the CornerRadius. You probably don't change the corner radius after app load, but if you do, you attach a new handler for the controls Loaded event. It's no big deal because it is only called once (at load time) - but anyway it looks bad. You could probably handle it like this:



public static void CornerRadiusChangedCallback(object sender, DependencyPropertyChangedEventArgs e)

Control control = sender as Control;

if (control == null) return;

control.Loaded -= Control_Loaded;
control.Loaded += Control_Loaded;




You should be aware that SystemDropShadowChrome is defined in more than one assembly (Aero and Aero2 for instance) so you'll have to link to the right assembly. Which one that is in use I think is system dependent(?). You could maybe use Reflection to overcome this(?)




Setting the properties of the Popup/Dropdown of the ComboBox at load time is a bad idea, because if the ComboBox changes size (width), the settings you made at load time are not adjusted to the new size. Instead you should make the settings for the ComboBox in both the Loaded and the SizeChanged events or maybe just in the DropDownOpened event.







share|improve this answer















share|improve this answer



share|improve this answer








edited Jun 22 at 10:40


























answered Jun 22 at 10:23









Henrik Hansen

3,7781417




3,7781417











  • Regarding SystemDropShadowChrome type: you are right. I can replace this line with Decorator shadowChrome = control.Template.FindName("shadow", control) as Decorator; and avoid extra DLLs dependency.
    – Vadim Ovchinnikov
    Jun 22 at 10:45











  • Regarding last point I don't know how practical it could be. I don't mind this feature, but dynamically changing size for ComboBox seems to be either bad UI experience or rare need. Usual ComboBoxes have fixed sizes. Am I missing something?
    – Vadim Ovchinnikov
    Jun 22 at 10:47










  • @VadimOvchinnikov: About SystemDropShadowChrome - Yes that's a way to go or simply just FrameworkElement :-)
    – Henrik Hansen
    Jun 22 at 10:49










  • @VadimOvchinnikov: Changing the with of a ComboBox in a dialog for instance or in a sizable docking window is very common and a very good UI experience - IMO :-)
    – Henrik Hansen
    Jun 22 at 10:52
















  • Regarding SystemDropShadowChrome type: you are right. I can replace this line with Decorator shadowChrome = control.Template.FindName("shadow", control) as Decorator; and avoid extra DLLs dependency.
    – Vadim Ovchinnikov
    Jun 22 at 10:45











  • Regarding last point I don't know how practical it could be. I don't mind this feature, but dynamically changing size for ComboBox seems to be either bad UI experience or rare need. Usual ComboBoxes have fixed sizes. Am I missing something?
    – Vadim Ovchinnikov
    Jun 22 at 10:47










  • @VadimOvchinnikov: About SystemDropShadowChrome - Yes that's a way to go or simply just FrameworkElement :-)
    – Henrik Hansen
    Jun 22 at 10:49










  • @VadimOvchinnikov: Changing the with of a ComboBox in a dialog for instance or in a sizable docking window is very common and a very good UI experience - IMO :-)
    – Henrik Hansen
    Jun 22 at 10:52















Regarding SystemDropShadowChrome type: you are right. I can replace this line with Decorator shadowChrome = control.Template.FindName("shadow", control) as Decorator; and avoid extra DLLs dependency.
– Vadim Ovchinnikov
Jun 22 at 10:45





Regarding SystemDropShadowChrome type: you are right. I can replace this line with Decorator shadowChrome = control.Template.FindName("shadow", control) as Decorator; and avoid extra DLLs dependency.
– Vadim Ovchinnikov
Jun 22 at 10:45













Regarding last point I don't know how practical it could be. I don't mind this feature, but dynamically changing size for ComboBox seems to be either bad UI experience or rare need. Usual ComboBoxes have fixed sizes. Am I missing something?
– Vadim Ovchinnikov
Jun 22 at 10:47




Regarding last point I don't know how practical it could be. I don't mind this feature, but dynamically changing size for ComboBox seems to be either bad UI experience or rare need. Usual ComboBoxes have fixed sizes. Am I missing something?
– Vadim Ovchinnikov
Jun 22 at 10:47












@VadimOvchinnikov: About SystemDropShadowChrome - Yes that's a way to go or simply just FrameworkElement :-)
– Henrik Hansen
Jun 22 at 10:49




@VadimOvchinnikov: About SystemDropShadowChrome - Yes that's a way to go or simply just FrameworkElement :-)
– Henrik Hansen
Jun 22 at 10:49












@VadimOvchinnikov: Changing the with of a ComboBox in a dialog for instance or in a sizable docking window is very common and a very good UI experience - IMO :-)
– Henrik Hansen
Jun 22 at 10:52




@VadimOvchinnikov: Changing the with of a ComboBox in a dialog for instance or in a sizable docking window is very common and a very good UI experience - IMO :-)
– Henrik Hansen
Jun 22 at 10:52












up vote
3
down vote














Holy crap. All that just to get Rounded corners?




Yes, I wholeheartedly agree with that comment. It is astonishing, how some things that sound trivial - are extremely complicated in WPF. But there is only so much that you can do. You can either fight it or embrace it (you can also choose different framework, but that's another story). And when choosing between those two options, you have to consider this:



What are the chances, that you are not going to change default templates anyway in the future? In my experience those chances are often quite slim. If you care enough about your UI that corner radius bothers you, then that's probably not the only thing you will need to change. What about that highlight color? What about other controls? Will you change corner radius of scrollbar buttons also? Progressbar?




Correct me if I'm wrong. For this you should 1) Store nearly a hundred
lines of code for default button styles, 2) Create new class with
attached property (you can't write Border.CornerRadius="10" for
button, can you?)




Yes, you can't, but you can set it in style. This code works:



<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Button>
<Button.Style>
<Style TargetType="Button">
<Setter Property="Border.CornerRadius" Value="15"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="100"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border CornerRadius="TemplateBinding Border.CornerRadius"
BorderThickness="1" Background="Green" BorderBrush="Black"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</Page>


But you can, of course, use your own stand-alone attached property (there is no need to subclass anything). You can even make it inherited, and set it for you entire MainWindow, so that all controls are affected.




Don't get me wrong, I often write similar behaviors myself. I just think that whenever I want to modify control template via behavior, it is a good idea to carefully consider the implications. Yes, when you override template, there is some initial copy-pasting. But then it just works. Want to databind Padding property? Fine. Want to change corner radius property via animation? Sure. Want to use those buttons inside a docked window, that can reload its content multiple times? No problem.



You solution on the other hand is very much tied to your current use case:



1) It overrides padding property, potentially breaking triggers and databindings.



2) It does not allow modifications to CornerRadius after control is loaded.



3) It will not work, if Loaded event triggers more than once. Most notably, your padding will just keep growing.



4) It relies heavily on the fact, that default button template has an element named border. But will it in the future versions of the framework? I mean realistically, it probably will, but this assumption still feels shaky.



There might be other edge cases, where you behavior will fail but the template will not. So is it all worth it? I am not so sure.






share|improve this answer























  • By the way, in this button template, are you missing default trigger implementation and if you don't include them, they won't work, am I right?
    – Vadim Ovchinnikov
    Jun 22 at 14:07










  • Regarding point #3 I suppose I would just set Padding to Border element and that will work even in case of multiple Loaded triggering. I don't understand why this won't work as desired.
    – Vadim Ovchinnikov
    Jun 22 at 14:10










  • @VadimOvchinnikov yes, it is missing pretty much everything, except for border. :) This was just an example of how you can replace hardcoded radius with TemplateBinding in your actual template.
    – Nikita B
    Jun 22 at 14:12










  • Regarding point #2 I agree but I assume this can be solved but I haven't tried because I won't be using this case.
    – Vadim Ovchinnikov
    Jun 22 at 14:13










  • Maybe it's offtopic, but I'm very curious about idea to "choose different framework". Are there any other powerful enterprise-level frameworks except WPF?
    – Vadim Ovchinnikov
    Jun 22 at 14:16














up vote
3
down vote














Holy crap. All that just to get Rounded corners?




Yes, I wholeheartedly agree with that comment. It is astonishing, how some things that sound trivial - are extremely complicated in WPF. But there is only so much that you can do. You can either fight it or embrace it (you can also choose different framework, but that's another story). And when choosing between those two options, you have to consider this:



What are the chances, that you are not going to change default templates anyway in the future? In my experience those chances are often quite slim. If you care enough about your UI that corner radius bothers you, then that's probably not the only thing you will need to change. What about that highlight color? What about other controls? Will you change corner radius of scrollbar buttons also? Progressbar?




Correct me if I'm wrong. For this you should 1) Store nearly a hundred
lines of code for default button styles, 2) Create new class with
attached property (you can't write Border.CornerRadius="10" for
button, can you?)




Yes, you can't, but you can set it in style. This code works:



<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Button>
<Button.Style>
<Style TargetType="Button">
<Setter Property="Border.CornerRadius" Value="15"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="100"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border CornerRadius="TemplateBinding Border.CornerRadius"
BorderThickness="1" Background="Green" BorderBrush="Black"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</Page>


But you can, of course, use your own stand-alone attached property (there is no need to subclass anything). You can even make it inherited, and set it for you entire MainWindow, so that all controls are affected.




Don't get me wrong, I often write similar behaviors myself. I just think that whenever I want to modify control template via behavior, it is a good idea to carefully consider the implications. Yes, when you override template, there is some initial copy-pasting. But then it just works. Want to databind Padding property? Fine. Want to change corner radius property via animation? Sure. Want to use those buttons inside a docked window, that can reload its content multiple times? No problem.



You solution on the other hand is very much tied to your current use case:



1) It overrides padding property, potentially breaking triggers and databindings.



2) It does not allow modifications to CornerRadius after control is loaded.



3) It will not work, if Loaded event triggers more than once. Most notably, your padding will just keep growing.



4) It relies heavily on the fact, that default button template has an element named border. But will it in the future versions of the framework? I mean realistically, it probably will, but this assumption still feels shaky.



There might be other edge cases, where you behavior will fail but the template will not. So is it all worth it? I am not so sure.






share|improve this answer























  • By the way, in this button template, are you missing default trigger implementation and if you don't include them, they won't work, am I right?
    – Vadim Ovchinnikov
    Jun 22 at 14:07










  • Regarding point #3 I suppose I would just set Padding to Border element and that will work even in case of multiple Loaded triggering. I don't understand why this won't work as desired.
    – Vadim Ovchinnikov
    Jun 22 at 14:10










  • @VadimOvchinnikov yes, it is missing pretty much everything, except for border. :) This was just an example of how you can replace hardcoded radius with TemplateBinding in your actual template.
    – Nikita B
    Jun 22 at 14:12










  • Regarding point #2 I agree but I assume this can be solved but I haven't tried because I won't be using this case.
    – Vadim Ovchinnikov
    Jun 22 at 14:13










  • Maybe it's offtopic, but I'm very curious about idea to "choose different framework". Are there any other powerful enterprise-level frameworks except WPF?
    – Vadim Ovchinnikov
    Jun 22 at 14:16












up vote
3
down vote










up vote
3
down vote










Holy crap. All that just to get Rounded corners?




Yes, I wholeheartedly agree with that comment. It is astonishing, how some things that sound trivial - are extremely complicated in WPF. But there is only so much that you can do. You can either fight it or embrace it (you can also choose different framework, but that's another story). And when choosing between those two options, you have to consider this:



What are the chances, that you are not going to change default templates anyway in the future? In my experience those chances are often quite slim. If you care enough about your UI that corner radius bothers you, then that's probably not the only thing you will need to change. What about that highlight color? What about other controls? Will you change corner radius of scrollbar buttons also? Progressbar?




Correct me if I'm wrong. For this you should 1) Store nearly a hundred
lines of code for default button styles, 2) Create new class with
attached property (you can't write Border.CornerRadius="10" for
button, can you?)




Yes, you can't, but you can set it in style. This code works:



<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Button>
<Button.Style>
<Style TargetType="Button">
<Setter Property="Border.CornerRadius" Value="15"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="100"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border CornerRadius="TemplateBinding Border.CornerRadius"
BorderThickness="1" Background="Green" BorderBrush="Black"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</Page>


But you can, of course, use your own stand-alone attached property (there is no need to subclass anything). You can even make it inherited, and set it for you entire MainWindow, so that all controls are affected.




Don't get me wrong, I often write similar behaviors myself. I just think that whenever I want to modify control template via behavior, it is a good idea to carefully consider the implications. Yes, when you override template, there is some initial copy-pasting. But then it just works. Want to databind Padding property? Fine. Want to change corner radius property via animation? Sure. Want to use those buttons inside a docked window, that can reload its content multiple times? No problem.



You solution on the other hand is very much tied to your current use case:



1) It overrides padding property, potentially breaking triggers and databindings.



2) It does not allow modifications to CornerRadius after control is loaded.



3) It will not work, if Loaded event triggers more than once. Most notably, your padding will just keep growing.



4) It relies heavily on the fact, that default button template has an element named border. But will it in the future versions of the framework? I mean realistically, it probably will, but this assumption still feels shaky.



There might be other edge cases, where you behavior will fail but the template will not. So is it all worth it? I am not so sure.






share|improve this answer
















Holy crap. All that just to get Rounded corners?




Yes, I wholeheartedly agree with that comment. It is astonishing, how some things that sound trivial - are extremely complicated in WPF. But there is only so much that you can do. You can either fight it or embrace it (you can also choose different framework, but that's another story). And when choosing between those two options, you have to consider this:



What are the chances, that you are not going to change default templates anyway in the future? In my experience those chances are often quite slim. If you care enough about your UI that corner radius bothers you, then that's probably not the only thing you will need to change. What about that highlight color? What about other controls? Will you change corner radius of scrollbar buttons also? Progressbar?




Correct me if I'm wrong. For this you should 1) Store nearly a hundred
lines of code for default button styles, 2) Create new class with
attached property (you can't write Border.CornerRadius="10" for
button, can you?)




Yes, you can't, but you can set it in style. This code works:



<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Button>
<Button.Style>
<Style TargetType="Button">
<Setter Property="Border.CornerRadius" Value="15"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="100"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border CornerRadius="TemplateBinding Border.CornerRadius"
BorderThickness="1" Background="Green" BorderBrush="Black"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</Page>


But you can, of course, use your own stand-alone attached property (there is no need to subclass anything). You can even make it inherited, and set it for you entire MainWindow, so that all controls are affected.




Don't get me wrong, I often write similar behaviors myself. I just think that whenever I want to modify control template via behavior, it is a good idea to carefully consider the implications. Yes, when you override template, there is some initial copy-pasting. But then it just works. Want to databind Padding property? Fine. Want to change corner radius property via animation? Sure. Want to use those buttons inside a docked window, that can reload its content multiple times? No problem.



You solution on the other hand is very much tied to your current use case:



1) It overrides padding property, potentially breaking triggers and databindings.



2) It does not allow modifications to CornerRadius after control is loaded.



3) It will not work, if Loaded event triggers more than once. Most notably, your padding will just keep growing.



4) It relies heavily on the fact, that default button template has an element named border. But will it in the future versions of the framework? I mean realistically, it probably will, but this assumption still feels shaky.



There might be other edge cases, where you behavior will fail but the template will not. So is it all worth it? I am not so sure.







share|improve this answer















share|improve this answer



share|improve this answer








edited Jun 22 at 14:08


























answered Jun 22 at 13:49









Nikita B

12.3k11551




12.3k11551











  • By the way, in this button template, are you missing default trigger implementation and if you don't include them, they won't work, am I right?
    – Vadim Ovchinnikov
    Jun 22 at 14:07










  • Regarding point #3 I suppose I would just set Padding to Border element and that will work even in case of multiple Loaded triggering. I don't understand why this won't work as desired.
    – Vadim Ovchinnikov
    Jun 22 at 14:10










  • @VadimOvchinnikov yes, it is missing pretty much everything, except for border. :) This was just an example of how you can replace hardcoded radius with TemplateBinding in your actual template.
    – Nikita B
    Jun 22 at 14:12










  • Regarding point #2 I agree but I assume this can be solved but I haven't tried because I won't be using this case.
    – Vadim Ovchinnikov
    Jun 22 at 14:13










  • Maybe it's offtopic, but I'm very curious about idea to "choose different framework". Are there any other powerful enterprise-level frameworks except WPF?
    – Vadim Ovchinnikov
    Jun 22 at 14:16
















  • By the way, in this button template, are you missing default trigger implementation and if you don't include them, they won't work, am I right?
    – Vadim Ovchinnikov
    Jun 22 at 14:07










  • Regarding point #3 I suppose I would just set Padding to Border element and that will work even in case of multiple Loaded triggering. I don't understand why this won't work as desired.
    – Vadim Ovchinnikov
    Jun 22 at 14:10










  • @VadimOvchinnikov yes, it is missing pretty much everything, except for border. :) This was just an example of how you can replace hardcoded radius with TemplateBinding in your actual template.
    – Nikita B
    Jun 22 at 14:12










  • Regarding point #2 I agree but I assume this can be solved but I haven't tried because I won't be using this case.
    – Vadim Ovchinnikov
    Jun 22 at 14:13










  • Maybe it's offtopic, but I'm very curious about idea to "choose different framework". Are there any other powerful enterprise-level frameworks except WPF?
    – Vadim Ovchinnikov
    Jun 22 at 14:16















By the way, in this button template, are you missing default trigger implementation and if you don't include them, they won't work, am I right?
– Vadim Ovchinnikov
Jun 22 at 14:07




By the way, in this button template, are you missing default trigger implementation and if you don't include them, they won't work, am I right?
– Vadim Ovchinnikov
Jun 22 at 14:07












Regarding point #3 I suppose I would just set Padding to Border element and that will work even in case of multiple Loaded triggering. I don't understand why this won't work as desired.
– Vadim Ovchinnikov
Jun 22 at 14:10




Regarding point #3 I suppose I would just set Padding to Border element and that will work even in case of multiple Loaded triggering. I don't understand why this won't work as desired.
– Vadim Ovchinnikov
Jun 22 at 14:10












@VadimOvchinnikov yes, it is missing pretty much everything, except for border. :) This was just an example of how you can replace hardcoded radius with TemplateBinding in your actual template.
– Nikita B
Jun 22 at 14:12




@VadimOvchinnikov yes, it is missing pretty much everything, except for border. :) This was just an example of how you can replace hardcoded radius with TemplateBinding in your actual template.
– Nikita B
Jun 22 at 14:12












Regarding point #2 I agree but I assume this can be solved but I haven't tried because I won't be using this case.
– Vadim Ovchinnikov
Jun 22 at 14:13




Regarding point #2 I agree but I assume this can be solved but I haven't tried because I won't be using this case.
– Vadim Ovchinnikov
Jun 22 at 14:13












Maybe it's offtopic, but I'm very curious about idea to "choose different framework". Are there any other powerful enterprise-level frameworks except WPF?
– Vadim Ovchinnikov
Jun 22 at 14:16




Maybe it's offtopic, but I'm very curious about idea to "choose different framework". Are there any other powerful enterprise-level frameworks except WPF?
– Vadim Ovchinnikov
Jun 22 at 14:16












 

draft saved


draft discarded


























 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f197042%2frounded-borders-for-different-controls-button-textbox-combobox-via-attached%23new-answer', 'question_page');

);

Post as a guest













































































Popular posts from this blog

Greedy Best First Search implementation in Rust

Function to Return a JSON Like Objects Using VBA Collections and Arrays

C++11 CLH Lock Implementation