📜 ⬆️ ⬇️

An example of the principle of DRY in Windows Phone 7

I am an idealist.


Software developers are hard to be an idealist - code optimization never stops. There are always, a little later, more elegant solutions, which leads to constant code refactoring. If the project has a limited time, it somehow stops the process of continuous improvement, but in the case of personal development, the programmer is limited only by his knowledge, which, in turn, is also constantly growing, which turns into a vicious circle.

But, let's move a little from philosophy to practice. Developing applications, I always strived for the ideal, sought to follow the current concepts. In the course of development, I always tried to follow the principle of DRY. Some time ago I started developing under Windows Phone. As a result, “wrappers” appeared for the operations that are used most often. Some of them want to share.

DRY and why it is needed


DRY - Don`t Repeat Yourself.
This is the principle of software development, aimed at reducing the repetition of information of various kinds, especially in systems with multiple layers of abstraction. The principle of DRY is formulated as: “Each piece of knowledge must have a single, consistent and authoritative representation within the system.”
Wikipedia

In each project there are standard operations that are repeated from one project to another. It would seem not so difficult to write a few lines of code, but this is not correct. This approach increases development time and the likelihood of repeating errors. It is not necessary each time to describe the same thing, the same error interceptions, the same messages, the same code from the project to the project, it would be more correct to take standard operations to a certain library and simply use it.
Thus, we:

Library


I am sure that many people have such practices. This library is written based on my personal needs. I want to note that with the release of Windows Phone 8 and Visual Studio 2012, as well as updates for Visual Studio 2010 and Windows Phone 7.5, these examples no longer have much practical benefit, since such operations have been replaced by async / await . But to demonstrate the principle of DRY is quite suitable.
Library source code is available at http://vb.codeplex.com

So, in the library there are two classes, one for working with asynchronous reading of resources, the second for working with local file storage. Each class contains its own methods, properties, events, and error trapping.
Consider in more detail.
')
Class LoadManager
Constructor
Loadmanagerpublic class LoadManager

Initializes a new instance of the object.
Methods
Loadpublic void Load (string url)

Creates a new WebClient object and calls its DownloadStringAsync method.
Properties
Encodingpublic int Encoding

Indicates which encoding to use when reading data.
Important! To work with different encodings, the library MSPToolkit.dll is used. She has already been added to the project.
This parameter is optional, but sometimes, for example, to work with the 1251 encoding, it must be used.
Savetopublic string SaveTo

The name of the local file to which the data will be saved.
The file is stored in the application’s isolated storage.
Developments
Oncancelpublic event System.Action OnCancel

Triggered when canceling data download.
OnErrorpublic event System.Action <Exception> OnError

Fails when data loading error.
Onfinishpublic event System.Action OnFinish

It works at the end of the download, regardless of whether there were errors or not.
Convenient to use to hide the download progress bar.
Onloadpublic event System.Action <string> OnLoad

It works when the data is successfully loaded.
Data comes in the form of a string.
OnNoNetworkpublic event System.Action OnNoNetwork

It works in the absence of a network.
Of course, if the network is found, but there is no Internet access, then the OnError event will work, with a message that the resource is unavailable.
OnProgresspublic event System.Action <DownloadProgressChangedEventArgs> OnProgress

It works when the download progress changes.
Onstartpublic event System.Action OnStart

It works at the start of data download.
Convenient to use to show the download progress bar.
Class FileManager
Constructor
Filemanagerpublic class FileManager

Initializes a new instance of the object.
Methods
Readpublic void Read (string FileName)

Opens a file for reading.
Accepts the file name as a string.
Savepublic void Save (string FileName, string Data)

Opens an existing file for writing or creates a new one if it is missing.
Properties
WriteAfterpublic string WriteAfter

The string that will be added at the end of the data.
Not always XML has a format that serializes normally. For example, sometimes there is not enough root node. This problem can be solved more elegantly, but the simplest method is to wrap the data in an additional root node.
WriteBeforepublic string WriteBefore

The string that will be added at the beginning of the data (similar to WriteAfter).
Developments
OnReadErrorpublic event System.Action <Exception> OnReadError

It works when a file read error occurs.
OnReadFileMissingpublic event System.Action OnReadFileMissing

It works in the absence of a file that is trying to open for reading.
If this event was intercepted, the OnReadError event will no longer be raised.
OnReadReadypublic event System.Action <StreamReader> OnReadReady

It works when the file is open for reading. Sends an open file descriptor.
OnSaveErrorpublic event System.Action <Exception> OnSaveError

Fails when writing a file.

Simple examples

Example of using LoadManager:
LoadManager DataLoader = new LoadManager(); DataLoader.OnLoad += new Action<string>(DataLoader_OnLoad); DataLoader.Load(resorce_url); void DataLoader_OnLoad(string data) { try { Deployment.Current.Dispatcher.BeginInvoke( delegate { // do something with data string }); } catch (Exception ex) { Deployment.Current.Dispatcher.BeginInvoke( delegate { MessageBox.Show(ex.Message, "Exception", MessageBoxButton.OK); }); } } 

An example of using FileManager:
 FileManager CacheFile = new FileManager(); CacheFile.OnReadReady += new Action<StreamReader>(File_OnReadOpen); CacheFile.Read(file_name); void File_OnReadOpen(StreamReader Stream) { // do something with file stream } 

It would seem nothing complicated and special. An additional level of abstraction has been added and the code has been moved to a separate library. But now this code, which is already written and ready for use, can be easily applied to other projects!

Real example


For example, we will develop a currency rate informer that will update the data in XML format from a third-party server. It is not interesting to just read XML and output data, so in addition the result will be cached locally, so that the exchange rate can be viewed without the Internet. Of course, it will not be relevant, but it is better to show the last updated result, rather than just an empty line.

I will not list all the files, as the project source code is available at http://exchangeexample.codeplex.com . I will focus only on the key points where the library was used.

So, the scheme is as follows:


Read local file
 //    public void LoadData() { //    FileManager CacheFile = new FileManager(); //        CacheFile.OnReadReady += new Action<StreamReader>(File_OnReadOpen); //     ,    CacheFile.OnReadFileMissing += new Action(File_OnReadFileMissing); //     CacheFile.Read(Common.Constants.ExchangeTmpFile); } //      void File_OnReadFileMissing() { //       Deployment.Current.Dispatcher.BeginInvoke( delegate { MessageBox.Show("Local file missing, first time application start?", "FileManager OnReadFileMissing exception", MessageBoxButton.OK); }); } //        void File_OnReadOpen(StreamReader Stream) { using (XmlReader XmlReader = XmlReader.Create(Stream)) { //  XML      XmlSerializer DataSerializer = new XmlSerializer(typeof(RatesList)); _RatesList = (RatesList)DataSerializer.Deserialize(XmlReader); Rates = _RatesList.Collection; } } 


Update local file
 //    public void UpdateData() { //    LoadManager DataLoader = new LoadManager(); //       DataLoader.OnNoNetwork += new Action(DataLoader_OnNoNetwork); //       DataLoader.OnError += new Action<Exception>(DataLoader_OnError); //        DataLoader.OnStart += new Action(DataLoader_OnStart); //         DataLoader.OnLoad += new Action<string>(DataLoader_OnLoad); //        DataLoader.OnFinish += new Action(DataLoader_OnFinish); //    ,       XML  //  DataLoader.OnSaveTo += new Action<string>(DataLoader_OnSaveTo); //      DataLoader.SaveTo = Common.Constants.ExchangeTmpFile; //    DataLoader.Load(Common.Constants.ExchangeApiUrl); } //           //     //   ,     as is void DataLoader_OnSaveTo(string data) { //     FileManager CacheFile = new FileManager(); //   (  )    CacheFile.WriteBefore = "<Root>"; //   (  )    CacheFile.WriteAfter = "</Root>"; //    ,    CacheFile.Save(Common.Constants.ExchangeTmpFile, data); } //      void DataLoader_OnNoNetwork() { Deployment.Current.Dispatcher.BeginInvoke( delegate { MessageBox.Show("No network available.", "LoadManager OnNoNetwork exception", MessageBoxButton.OK); }); } //     void DataLoader_OnError(Exception e) { Deployment.Current.Dispatcher.BeginInvoke( delegate { MessageBox.Show(e.Message, "LoadManager OnError exception", MessageBoxButton.OK); }); } //      void DataLoader_OnStart() { //     IsProgressVisible = true; //       IsDataLoaded = false; } //    ,      void DataLoader_OnFinish() { //     IsProgressVisible = false; } //    ,        void DataLoader_OnLoad(string data) { try { Deployment.Current.Dispatcher.BeginInvoke( delegate { //     LoadData(); //      LastUpdate = DateTime.Now; }); } catch (Exception ex) { Deployment.Current.Dispatcher.BeginInvoke( delegate { MessageBox.Show(ex.Message, "LoadManager OnLoad outer exception", MessageBoxButton.OK); }); } } 


We specify a context
  public partial class MainPage : PhoneApplicationPage { public MainPage() { InitializeComponent(); //     DataContext = App.MainViewModel; //     Loaded += new RoutedEventHandler(MainPage_Loaded); } //     void MainPage_Loaded(object sender, RoutedEventArgs e) { //    ,    UI Deployment.Current.Dispatcher.BeginInvoke( delegate { //    App.MainViewModel.UpdateData(); }); } } 


XAML for binding and data output
  <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <ListBox Grid.Row="0" ItemsSource="{Binding Rates}"> <ListBox.ItemContainerStyle> <Style TargetType="ListBoxItem"> <Setter Property="HorizontalContentAlignment" Value="Stretch"/> </Style> </ListBox.ItemContainerStyle> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Margin="0,0,0,17"> <StackPanel Orientation="Horizontal"> <TextBlock Text="1" Style="{StaticResource PhoneTextLargeStyle}" Foreground="{StaticResource PhoneAccentBrush}" /> <TextBlock Text="{Binding Currency}" Style="{StaticResource PhoneTextLargeStyle}" Foreground="{StaticResource PhoneAccentBrush}"/> </StackPanel> <Grid HorizontalAlignment="Stretch"> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="1*"/> </Grid.ColumnDefinitions> <StackPanel Grid.Column="0"> <TextBlock Text="Buy" Style="{StaticResource PhoneTextNormalStyle}"/> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Buy}" Style="{StaticResource PhoneTextLargeStyle}"/> <TextBlock Text="UAH" Style="{StaticResource PhoneTextLargeStyle}" Opacity="0.5" /> </StackPanel> </StackPanel> <StackPanel Grid.Column="1"> <TextBlock Text="Sale" Style="{StaticResource PhoneTextNormalStyle}" /> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Sale}" Style="{StaticResource PhoneTextLargeStyle}"/> <TextBlock Text="UAH" Style="{StaticResource PhoneTextLargeStyle}" Opacity="0.5"/> </StackPanel> </StackPanel> </Grid> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,12,0,0"> <TextBlock Text="Last update:" Style="{StaticResource PhoneTextNormalStyle}" /> <TextBlock Text="{Binding LastUpdate}" Style="{StaticResource PhoneTextAccentStyle}" /> </StackPanel> </Grid> 



The result will look something like this:
It took much more time to write an article than on the test application itself :)

Related Links:

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


All Articles