- Details
- Written by: Stanko Milosev
- Category: WPF
- Hits: 5862
While I was learning inversion of control from autofacs example, I got idea to write my application which I will use to "feel" IoC.
So, idea was to have two buttons where one will write a message to console, and second one will write same message to text box window. Idea looked simple, but... Problem was because I wanted to follow MVVM pattern, I have created command, binded click event, then all I had to do was to change textbox text, and that was actually a problem, I couldn't find a way simply to change the value...
Only way which I found was to send an object as parameter, something like:
CommandParameter="{Binding ElementName=IoCmessageDisplayTextBox}"
Where IoCmessageDisplayTextBox is text box on which I want to display my message.
So, here is my XAML:
<Window x:Class="IoCtest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:IoCtest" Title="MainWindow" Height="350" Width="525"> <Window.DataContext><local:ClickModel /> </Window.DataContext> <Grid OverridesDefaultStyle="True"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="*"></RowDefinition> <RowDefinition Height="30"></RowDefinition> </Grid.RowDefinitions> <TextBox x:Name="IoCmessageDisplayTextBox" Grid.Row="0" Grid.Column="0" TextWrapping="Wrap" Text="TextBox" AcceptsReturn="True" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible"/> <Button Grid.Row="1" Content="Write to memo" Command="{Binding MyClick}" CommandParameter="{Binding ElementName=IoCmessageDisplayTextBox}" Height="30" VerticalAlignment="Bottom"></Button> </Grid> </Window>
Since I already explained how to create button click event using ICommand here, I will not do it again, I will just keep focus on method which will be executed when user clicks button.
Method looks like this:
public void ShowMessage(object obj) { TextBox IoCmessageDisplayTextBox = new TextBox(); IoCmessageDisplayTextBox = (TextBox)obj; IoCmessageDisplayTextBox.Text = "Hello world!"; }
As you can see I had to convert object which I sent as parameter to TextBox, and then I was able to change text property of my TextBox.
Example you download from here, project is prepared for IoC learning, that is why it is more dirty then usual :) I hope that results of my learning I will post soon.
- Details
- Written by: Stanko Milosev
- Category: WPF
- Hits: 5693
One example of button and textbox stretching together to fill view:
<Window x:Class="IoCtest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:IoCtest" Title="MainWindow" Height="350" Width="525"> <Grid OverridesDefaultStyle="True"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="*"></RowDefinition> <RowDefinition Height="30"></RowDefinition> </Grid.RowDefinitions> <TextBox Grid.Row="0" Grid.Column="0" TextWrapping="Wrap" Text="TextBox" AcceptsReturn="True" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible"/> <Button Grid.Row="1" Content="Write to memo" Height="30" VerticalAlignment="Bottom"></Button> </Grid> </Window>
Important lines to notice are:
<Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="*"></RowDefinition> <RowDefinition Height="30"></RowDefinition> </Grid.RowDefinitions>
and attributes:
Grid.Row="0" Grid.Column="0"
and
Grid.Row="1" VerticalAlignment="Bottom"
- Details
- Written by: Stanko Milosev
- Category: WPF
- Hits: 6975
To write this article I was using this article.
To show examples of dependency injection, I will implement application which will on one button show some message and on the other button same that message it will save to file. Method for showing message will be called ShowMessage, method for saving mesage will be called WriteMessageToFile.
So, first I have created new WPF application and I called it DependencyInjectionExample. Then I prepared XAML, where I added page controls, with which I separated DI and non DI examples, so my XAML looks like this:
<Window x:Class="DependencyInjectionExample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <StackPanel> <TabControl> <TabItem Header="Without Dependency Injection"> <Grid Background="#FFE5E5E5"> <StackPanel> <Button> Show message </Button> <Button> Write message to a file </Button> </StackPanel> </Grid> </TabItem> <TabItem Header="Inversion of Control"> <Grid Background="#FFE5E5E5"> <StackPanel> <Button> Show message </Button> <Button> Write message to a file </Button> </StackPanel> </Grid> </TabItem> <TabItem Header="Dependency Injection - constructor"> <Grid Background="#FFE5E5E5"> <StackPanel> <Button> Show message </Button> <Button> Write message to a file </Button> </StackPanel> </Grid> </TabItem> </TabControl> </StackPanel> </Grid> </Window>
My window now looks like this:
After that I prepared folders in which I will implement all my examples, so my solution looks like this:
Second step is to create example without dependency injection. Since I am using MVVM approach, I will create ICommand functionality which I will attach it to XAML, as I already explained it here.
Idea is to have one class (in my case DoTheMessage), which will react on Notify method of same class - once to display message, once to save message to a file.
Class which reacts on button click looks like this:
public class WithoutDiViewModelShowMessage { private ICommand _buttonCommand; DoTheMessageWithoutDi _doTheMessage = new DoTheMessageWithoutDi(); public ICommand ButtonClick { get { return _buttonCommand; } set { _buttonCommand = value; } } public WithoutDiViewModelShowMessage() { ButtonClick = new RelayCommand(new Action<object>(_doTheMessage.ShowMesage)); } } public class WithoutDiViewModelWriteMesage { private ICommand _buttonCommand; DoTheMessage _doTheMessage = new DoTheMessage(); public ICommand ButtonClick { get { return _buttonCommand; } set { _buttonCommand = value; } } public WithoutDiViewModelWriteMesage() { ButtonClick = new RelayCommand(new Action<object>(_doTheMessage.WriteMessage)); } }
Where part of XAML for writing / showing message without DI looks like this:
<TabItem Header="Without Dependency Injection"> <Grid Background="#FFE5E5E5"> <StackPanel> <StackPanel.Resources> <withoutDi:WithoutDiViewModelShowMessage x:Key="WithoutDiViewModelShowMessage" /> <withoutDi:WithoutDiViewModelWriteMesage x:Key="WithoutDiViewModelWriteMesage" /> </StackPanel.Resources> <Button DataContext="{StaticResource WithoutDiViewModelShowMessage}" Command="{Binding ButtonClick}"> Show message </Button> <Button DataContext="{StaticResource WithoutDiViewModelWriteMesage}" Command="{Binding ButtonClick}"> Write message to a file </Button> </StackPanel> </Grid> </TabItem>
And class which actually show / write messages looks like this:
class ActOnMessage { public void ShowMessage(string message) { MessageBox.Show(message); } public void WriteMessageToFile(object obj) { string _exePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location); string _file = _exePath + "\\helloWorld.txt"; try { TextWriter tw = new StreamWriter(_file); // write a line of text to the file tw.WriteLine("test"); // close the stream tw.Close(); } catch { MessageBox.Show("Unable to write file to: " + _file); } finally { MessageBox.Show("Mesage was successfully writen to: " + _file); } } } class DoTheMessageWithoutDi { private ActOnMessage _actOnMessage = null; public void ShowMesage(object o) { if (_actOnMessage == null) { _actOnMessage = new ActOnMessage(); } _actOnMessage.ShowMessage("Hello world!"); } public void WriteMessage(object o) { if (_actOnMessage == null) { _actOnMessage = new ActOnMessage(); } _actOnMessage.WriteMessageToFile("Hello world!"); } }
As you can see I had to write to methods (WithoutDiViewModelShowMessage and WithoutDiViewModelWriteMesage), under the "DoTheMessageWithoutDi" class for writing and showing messages, which is not good since I would like to have one class and one method which will react on the message.
Now lets see solution with Inversion of Control (IoC). My IoC.cs looks like this:
public interface INofificationAction { void ActOnNotification(string message); } //showMessage implementation class ShowMessage: INofificationAction { public void ActOnNotification(string message) { MessageBox.Show(message); } } //writeMessageToFile implementation class WriteMessageToFile : INofificationAction { public void ActOnNotification(string message) { string _exePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location); string _file = _exePath + "\\helloWorld.txt"; try { TextWriter tw = new StreamWriter(_file); // write a line of text to the file tw.WriteLine("test"); // close the stream tw.Close(); } catch { MessageBox.Show("Unable to write file to: " + _file); } finally { MessageBox.Show("Mesage was successfully writen to: " + _file); } } } class DoTheMessageIoC { private INofificationAction _actOnMessage = null; public void ShowMesage(object o) { if (_actOnMessage == null) { _actOnMessage = new ShowMessage(); } _actOnMessage.ActOnNotification("Hello world!"); } public void WriteMessage(object o) { if (_actOnMessage == null) { _actOnMessage = new WriteMessageToFile(); } _actOnMessage.ActOnNotification("Hello world!"); } }
First I implemented interface (INofificationAction), and then on the end, I assigned different methods to a interface.
Now let us see how it will look like with dependecy injection - constructor injection.
This is how my model now looks like:
//interface public interface INofificationAction { void ActOnNotification(string message); } //showMessage implementation class ShowMessage : INofificationAction { public void ActOnNotification(string message) { MessageBox.Show(message); } } //writeMessageToFile implementation class WriteMessageToFile : INofificationAction { public void ActOnNotification(string message) { string _exePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location); string _file = _exePath + "\\helloWorld.txt"; try { TextWriter tw = new StreamWriter(_file); // write a line of text to the file tw.WriteLine("test"); // close the stream tw.Close(); } catch { MessageBox.Show("Unable to write file to: " + _file); } finally { MessageBox.Show("Mesage was successfully writen to: " + _file); } } } class DoTheMessageIoC { private INofificationAction _actOnMessage = null; public DoTheMessageIoC(INofificationAction concreteImplementation) { this._actOnMessage = concreteImplementation; } public void Notify(object o) { _actOnMessage.ActOnNotification("Hello world!"); } }
As you can see last class (DoTheMessageIoC) has constructor which expects INofificationAction as parameter. And ViewModel now looks like:
public class DiConstructorViewModelShowMessage { private static ShowMessage writer = new DependencyInjectionExample.DiConstructor.ShowMessage(); private ICommand _buttonCommand; private DoTheMessageIoC _doTheMessage = new DoTheMessageIoC(writer); public ICommand ButtonClick { get { return _buttonCommand; } set { _buttonCommand = value; } } public DiConstructorViewModelShowMessage() { ButtonClick = new DiConstructorCommand(new Action<object>(_doTheMessage.Notify)); } } public class DiConstructorViewModelWriteMesage { private static WriteMessageToFile writer = new DependencyInjectionExample.DiConstructor.WriteMessageToFile(); private ICommand _buttonCommand; private DoTheMessageIoC _doTheMessage = new DoTheMessageIoC(writer); public ICommand ButtonClick { get { return _buttonCommand; } set { _buttonCommand = value; } } public DiConstructorViewModelWriteMesage() { ButtonClick = new DiConstructorCommand(new Action<object>(_doTheMessage.Notify)); } }
Here you can see for showMessage example:
private static ShowMessage writer = new DependencyInjectionExample.DiConstructor.ShowMessage();
private ICommand _buttonCommand;
private DoTheMessageIoC _doTheMessage = new DoTheMessageIoC(writer);
Example you can download from here (VS 2012).
- Details
- Written by: Stanko Milosev
- Category: WPF
- Hits: 4347
First model:
public class FirstModel { public string HelloWorld { get; set; } public FirstModel() { HelloWorld = "First model"; } }
Second model:
public class SecondModel { public string HelloWorld { get; set; } public SecondModel() { HelloWorld = "Second model"; } }
XAML:
<Window x:Class="TwoDataContext.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns:twoDataContext="clr-namespace:TwoDataContext" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid.Resources> <twoDataContext:FirstModel x:Key="FirstModel" /> <twoDataContext:SecondModel x:Key="SecondModel" /> </Grid.Resources> <TextBox DataContext = "{StaticResource FirstModel}" HorizontalAlignment="Left" Height="23" Margin="178,64,0,0" TextWrapping="Wrap" Text="{Binding HelloWorld}" VerticalAlignment="Top" Width="120"/> <TextBox DataContext = "{StaticResource SecondModel}" HorizontalAlignment="Left" Height="23" Margin="178,120,0,0" TextWrapping="Wrap" Text="{Binding HelloWorld}" VerticalAlignment="Top" Width="120"/> </Grid> </Window>
Example you can download from here.