📜 ⬆️ ⬇️

Extending the functionality of Windows controls with AttachedProperty



The cornerstone of developing applications for Windows (WPF, SilverLight, WP, WinRT) is the MVVM pattern. Which is based on the concept of data binding of the view model and user interface, which allows using the declarative UI description through XAML to get rid of codebehind (I did not invent / find the Russian translation) and transfer all the logic of working with the user interface to the view model.

Unfortunately, it is physically impossible for the manufacturer to implement all possible functions in the frameworks, and often there is a situation when it is impossible to solve the required task with the available means. If the problem is simple and one-time, then it is solved quickly in the place of origin, through the codebehind of the submission. But if the same functionality is needed in many places, you need to implement a convenient solution reuse mechanism.
')
I was prompted to write this article by the article "Automatic selection of links in universal Windows applications." The article found a solution to a specific problem and proposed a working solution. However, to use it, you need to call code in each codebehind for each text block. Moreover, if the data suggest a change in the process of work, it is necessary to monitor their change. In the course of my work, I meet such solutions quite often, they are different in implementation, but they are all distinguished by one constant feature, the complexity of supporting and maintaining the code.

To solve such problems, it is necessary to use attachable properties (AttachedProperty), this technology provides three capabilities necessary for solving the problem:

1) Store any value in the context of the control for which it was set
2) Notify on changing property data
3) Be used for declarative binding in XAML

We will solve the problem from the example above using the attached property. To do this, create a new static class named RtbEx and add a description of the new AttachedProperty to it:

public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached("Text", typeof(string), typeof(RtbEx), new PropertyMetadata(default(string))); public static void SetText(DependencyObject element, string value) { element.SetValue(TextProperty, value); } public static string GetText(DependencyObject element) { return (string) element.GetValue(TextProperty); } 

We set the property name to Text and the value type string. Separately, I will pay attention to the [Set | Get] Text methods they have been added to follow the recommended property declaration pattern and are intended to simplify access to the property value. Now we can use this property to store the data associated with the control.

 var someText = “Some Text”; RtbEx.SetText(richTextBlock, someText); someText = RtbEx.GetText(richTextBlock); 

But for the implementation of additional behavior, we need to do the work of parsing the text and forming the RTB content when the property changes. For this, you can define a handler for each property that is called each time the property value changes.
The event handler must be specified in the description of the attached property in the second parameter of the property metadata:

 public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached("Text", typeof(string), typeof(RtbEx), new PropertyMetadata(default(string), OnTextChanged)); 

The property change event handler must be of type PropertyChangedCallback

 private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var richTextBlock = d as RichTextBlock; if (richTextBlock == null) { return; } richTextBlock.Blocks.Clear(); var text = e.NewValue as string; if (string.IsNullOrWhiteSpace(text)) { return; } richTextBlock.Blocks.Add(CreateParagraph(text)); } 

The handler is as simple as possible; it determines that it is called for RichTextBlock, clears the data of the control, and if the new value of the property is not an empty string, it fills the control with new data.

 private static Paragraph CreateParagraph(string text) { var paragraph = new Paragraph(); var splitResult = Regex.Split(text, @"(https?://\S+)"); foreach (var part in splitResult) { if (part.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { var hyperLink = new Hyperlink {NavigateUri = new Uri(part)}; hyperLink.Inlines.Add(new Run {Text = part}); paragraph.Inlines.Add(hyperLink); continue; } paragraph.Inlines.Add(new Run {Text = part}); } return paragraph; } 

The RTB content generation code is not important in the context of the task and is far from ideal, it simply divides the string into blocks with links and simple text, and then builds the hierarchy of the representation of the RichTextBlock document.

The extension thus formed can be used from XAML using ordinary data bindings, without using additional code.

To do this, add a namespace containing the extension to the XAML document.

 xmlns:ex="using:RtbEx.Extensions" 

And set the binding using the usual {binding}

 <RichTextBlock ex:RtbEx.Text="{Binding SomeText}" FontSize="20"/> 


The use of attachable properties is limited only by your imagination, using them you can implement many convenient extensions that will greatly simplify the development, reuse and support of your code.

The test application code is available on Github: https://github.com/Viacheslav01/RtbEx

Source: https://habr.com/ru/post/254305/


All Articles