Today I am going to share a code snippet to create an Editable Button in WPF, where we can update the text of a Button at runtime. In this control the user would right-click a button and then click 'Edit' to make to control Editable, and later right-click it to save/cancel the change.
First step to make the Button editable is to change the Style of Button and add a TextBox inside it, which we will make visible when the user clicks on 'Edit'.
01.<Style TargetType="my:EditableButton">02. <Setter Property="FocusVisualStyle">03. <Setter.Value>04. <Style>05. <Setter Property="Control.Template">06. <Setter.Value>07. <ControlTemplate>08. <Rectangle Margin="2" SnapsToDevicePixels="True" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>09. </ControlTemplate>10. </Setter.Value>11. </Setter>12. </Style>13. </Setter.Value>14. </Setter>15. <Setter Property="Background" Value="#FFDDDDDD"/>16. <Setter Property="BorderBrush" Value="#FF707070"/>17. <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>18. <Setter Property="BorderThickness" Value="1"/>19. <Setter Property="HorizontalContentAlignment" Value="Center"/>20. <Setter Property="VerticalContentAlignment" Value="Center"/>21. <Setter Property="Padding" Value="1"/>22. <Setter Property="Template">23. <Setter.Value>24. <ControlTemplate TargetType="{x:Type Button}">25. <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">26. <Grid>27. <ContentPresenter x:Name="tbkContent" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>28. <TextBox Name="txtContent" Height="{TemplateBinding Height}" TextAlignment="Center" Visibility="Collapsed" Text="{TemplateBinding Content}" Padding="0" Background="Transparent" BorderBrush="Transparent" BorderThickness="0" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" Margin="{TemplateBinding Padding}"/>29. </Grid>30. </Border>31. <ControlTemplate.Triggers>32. <Trigger Property="IsDefaulted" Value="True">33. <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>34. </Trigger>35. <Trigger Property="IsMouseOver" Value="True">36. <Setter Property="Background" TargetName="border" Value="#FFBEE6FD"/>37. <Setter Property="BorderBrush" TargetName="border" Value="#FF3C7FB1"/>38. </Trigger>39. <Trigger Property="IsPressed" Value="True">40. <Setter Property="Background" TargetName="border" Value="#FFC4E5F6"/>41. <Setter Property="BorderBrush" TargetName="border" Value="#FF2C628B"/>42. </Trigger>43. <Trigger Property="ToggleButton.IsChecked" Value="True">44. <Setter Property="Background" TargetName="border" Value="#FFBCDDEE"/>45. <Setter Property="BorderBrush" TargetName="border" Value="#FF245A83"/>46. </Trigger>47. <Trigger Property="IsEnabled" Value="False">48. <Setter Property="Background" TargetName="border" Value="#FFF4F4F4"/>49. <Setter Property="BorderBrush" TargetName="border" Value="#FFADB2B5"/>50. <Setter Property="TextElement.Foreground" TargetName="tbkContent" Value="#FF838383"/>51. </Trigger>52. </ControlTemplate.Triggers>53. </ControlTemplate>54. </Setter.Value>55. </Setter>56.</Style>As you can see in the above code, I have added a "txtContent" textbox which is hidden. Also to make editing look like a part of the Button, the Background and BorderBrush properties of textbox are transparent
01.Public Class EditableButton02. Inherits Button03. Private mButtonContentPresenter As ContentPresenter04. Private mButtonTextBox As TextBox05. 06. Public Sub New()07. Me.DefaultStyleKey = GetType(EditableButton)08. AddHandler Me.Loaded, New RoutedEventHandler(AddressOf EditableButton_Loaded)09. End Sub10. 11. Private Sub EditableButton_Loaded(sender As Object, e As RoutedEventArgs)12. 13. Me.OnApplyTemplate()14. End Sub15. 16. Public Overrides Sub OnApplyTemplate()17. MyBase.OnApplyTemplate()18. mButtonContentPresenter = DirectCast(Me.GetTemplateChild("tbkContent"), ContentPresenter)19. mButtonTextBox = DirectCast(Me.GetTemplateChild("txtContent"), TextBox)20. CreateSaveContextMenu()21. CreateEditContextMenu()22. End Sub23. 24. Public Property IsEditMode() As Boolean25. Get26. Return CBool(GetValue(IsEditModeProperty))27. End Get28. Private Set(value As Boolean)29. SetValue(IsEditModeProperty, value)30. End Set31. End Property32. 33. ' Using a DependencyProperty as the backing store for IsEditMode. This enables animation, styling, binding, etc...34. Public Shared ReadOnly IsEditModeProperty As DependencyProperty = DependencyProperty.Register("IsEditMode", GetType(Boolean), GetType(EditableButton), New PropertyMetadata(False))35. 36. Private Sub CreateSaveContextMenu()37. Dim menu As New ContextMenu38. Dim itm As New MenuItem()39. itm.Header = "Save"40. AddHandler itm.Click, New RoutedEventHandler(AddressOf itmSave_Click)41. menu.Items.Add(itm)42. 43. itm = New MenuItem()44. itm.Header = "Cancel"45. AddHandler itm.Click, New RoutedEventHandler(AddressOf itmCancel_Click)46. menu.Items.Add(itm)47. 48. ContextMenuService.SetContextMenu(mButtonTextBox, menu)49. End Sub50. 51. 52. 53. Private Sub CreateEditContextMenu()54. Dim menu As New ContextMenu55. Dim itm As New MenuItem56. itm.Header = "Edit"57. AddHandler itm.Click, New RoutedEventHandler(AddressOf itmEdit_Click)58. menu.Items.Add(itm)59. 60. ContextMenuService.SetContextMenu(mButtonContentPresenter, menu)61. End Sub62. 63. Private Sub itmEdit_Click(sender As Object, e As RoutedEventArgs)64. mButtonContentPresenter.Visibility = Visibility.Collapsed65. mButtonTextBox.Visibility = Visibility.Visible66. IsEditMode = True67. End Sub68. 69. Private Sub itmSave_Click(sender As Object, e As RoutedEventArgs)70. mButtonContentPresenter.Visibility = Visibility.Visible71. mButtonTextBox.Visibility = Visibility.Collapsed72. Me.Content = mButtonTextBox.Text73. IsEditMode = False74. End Sub75. 76. Private Sub itmCancel_Click(sender As Object, e As RoutedEventArgs)77. mButtonContentPresenter.Visibility = Visibility.Visible78. mButtonTextBox.Visibility = Visibility.Collapsed79. IsEditMode = False80. End Sub81.End ClassIn the above code we have an "IsEditMode" property which the user can use to know whether the control is in Edit Mode or not. Also, we have two different Context Menus, one to Edit the Control, and the another to Save/Cancel the changes. Based on the menuitems that are clicked, we are going to set the visibility of TextBox and ContentPresenter, defined in XAML.
Now we can use the control in our application as below
01.<Window x:Class="MainWindow"03. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"04. xmlns:my="clr-namespace:WpfApplication1"05. Title="MainWindow" Height="350" Width="525">06. 07. <Grid>08. <my:EditableButton x:Name="MyButton" Width="100" Height="25" Content="Edit Me"/>09. </Grid>10.</Window>Hope the above control helps you in your development.
No comments:
Post a Comment