Archive for the ‘WPF’ Category.
Hey all,
I created an MVVM project and then saved it as a template. This way when I create new projects, I can simple choose MVVM Base and save a ton of time.
You can use my template if you want. You can download it here: MVVM Base.zip
If you want this to be available as a new project type in you Visual Studio install, follow these steps.
Hey all,
WPF provides a ProgressBar control. But there isn’t really a manual for it, especially if you want to follow MVVM.
So I am going to make a little application that counts from zero to ten and tracks the progress. You are going to see when it is OK to use the foreground and when it is not OK but better to use BackgroundWorker.
While much of this code may be production ready, you should be aware that this code intentionally implements a foreground process that is an example of what not to do.
There are two basic classes used for MVVM.
These are found on different blogs and different posts all over the internet, so I would say they are public domain, or free and unlicensed.
class ProgressBarViewModel : ViewModelBase { }
This will be populated as we create our View.
Ok, so lets create the GUI.
Here is the XAML.
<Window x:Class="WPFProgressBarUsingBackgroundWorker.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WPFProgressBarUsingBackgroundWorker" Title="MainWindow" > <Window.Resources> <local:ProgressBarViewModel x:Key="PBVM" /> </Window.Resources> <Grid> <StackPanel> <Label Content="{Binding Path=Value}" DataContext="{StaticResource ResourceKey=PBVM}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" Name="labelNumberCounter" VerticalAlignment="Center" FontSize="175" /> <ProgressBar Margin="0,3,0,3" Height="20" Name="progressBar" Value="{Binding Path=Value}" DataContext="{StaticResource ResourceKey=PBVM}" Minimum="{Binding Min}" Maximum="{Binding Max}"/> <Button Command="{Binding Path=IncrementBy1}" Content="Manual Count" DataContext="{StaticResource PBVM}" Height="23" IsEnabled="{Binding Path=IsNotInProgress}" Name="button1" Width="Auto" /> <Button Margin="0,3,0,3" IsEnabled="{Binding Path=IsNotInProgress}" Command="{Binding Path=IncrementAsForegroundProcess}" DataContext="{StaticResource ResourceKey=PBVM}" Content="Count to 10 as a foreground process" HorizontalAlignment="Stretch" Height="23" Name="buttonForeground" VerticalAlignment="Top" Width="Auto" /> <Button Margin="0,3,0,3" IsEnabled="{Binding Path=IsNotInProgress}" Command="{Binding Path=IncrementAsBackgroundProcess}" DataContext="{StaticResource ResourceKey=PBVM}" Content="Count to 10 as a background process" HorizontalAlignment="Stretch" Height="23" Name="buttonBackground" VerticalAlignment="Top" Width="Auto" /> <Button Command="{Binding Path=ResetCounter}" Content="Reset" DataContext="{StaticResource PBVM}" Height="23" IsEnabled="{Binding Path=IsNotInProgress}" Name="buttonReset" Width="Auto" /> </StackPanel> </Grid> </Window>
Here is the code for the ProgressBarViewModel.cs
using System; using System.Collections.Generic; using System.ComponentModel; using System.Threading; using System.Text; using System.Windows.Input; using MVVM; namespace WPFProgressBarUsingBackgroundWorker { class ProgressBarViewModel : ViewModelBase { #region Member Fields Double _Value; bool _IsInProgress; int _Min = 0, _Max = 10; #endregion #region Member RelayCommands that implement ICommand RelayCommand _Increment; RelayCommand _IncrementBy1; RelayCommand _IncrementAsBackgroundProcess; RelayCommand _ResetCounter; #endregion #region Constructors /// <summary> /// The default constructor /// </summary> public ProgressBarViewModel() { } #endregion #region Properties /// <summary> /// Used to mark if the counter is in progress so the counter can't be started /// while it is already running. /// </summary> public bool IsInProgress { get { return _IsInProgress; } set { _IsInProgress = value; NotifyPropertyChanged("IsInProgress"); NotifyPropertyChanged("IsNotInProgress"); } } public bool IsNotInProgress { get { return !IsInProgress; } } public int Max { get { return _Max; } set { _Max = value; NotifyPropertyChanged("Max"); } } public int Min { get { return _Min; } set { _Min = value; NotifyPropertyChanged("Min"); } } /// <summary> /// This is the Value. The Counter should display this. /// </summary> public Double Value { get { return _Value; } set { if (value <= _Max) { if (value >= _Min) { _Value = value; } else { _Value = _Min; } } else { _Value = _Max; } NotifyPropertyChanged("Value"); } } #region ICommand Properties /// <summary> /// An ICommand representation of the Increment() function. /// </summary> public ICommand IncrementBy1 { get { if (_IncrementBy1 == null) { _IncrementBy1 = new RelayCommand(param => this.Increment()); } return _IncrementBy1; } } /// <summary> /// An ICommand representation of the IncrementProgressForegroundWorker() function. /// </summary> public ICommand IncrementAsForegroundProcess { get { if (_Increment == null) { _Increment = new RelayCommand(param => this.IncrementProgressForeground()); } return _Increment; } } /// <summary> /// An ICommand representation of the IncrementProgressForeground() function. /// </summary> public ICommand IncrementAsBackgroundProcess { get { if (_IncrementAsBackgroundProcess == null) { _IncrementAsBackgroundProcess = new RelayCommand(param => this.IncrementProgressBackgroundWorker()); } return _IncrementAsBackgroundProcess; } } /// <summary> /// An ICommand representation of the Reset() function. /// </summary> public ICommand ResetCounter { get { if (_ResetCounter == null) { _ResetCounter = new RelayCommand(param => this.Reset()); } return _ResetCounter; } } #endregion ICommand Properties #endregion #region Functions /// <summary> /// This function manually increments the counter by 1 in the foreground. /// Because it only increments by one, the WPF control bound to Value will /// display the new value when this function completes. /// </summary> public void Increment() { // If we are in progress already, don't do anything if (IsInProgress) return; // If the value is already at 10, start the counting over. if (Value == 10) Reset(); Value++; } /// <summary> /// This function starts the counter as a foreground process. /// This doesn't work. It counts to 10 but the UI is not updated /// until the function completes. This is especially problematic /// since the buttons are left enabled. /// </summary> public void IncrementProgressForeground() { // If we are in progress already, don't do anything if (IsInProgress) return; Reset(); IsInProgress = true; Value = 0; for (int i = _Min; i < _Max; i++) { Value++; Thread.Sleep(1000); } IsInProgress = false; } /// <summary> /// This starts the counter as a background process. /// </summary> public void IncrementProgressBackgroundWorker() { // If we are in progress already, don't do anything if (IsInProgress) return; Reset(); IsInProgress = true; BackgroundWorker worker = new BackgroundWorker(); // Configure the function that will run when started worker.DoWork += new DoWorkEventHandler(worker_DoWork); /*The progress reporting is not needed with this implementation and is therefore commented out. However, in your more complex application, you may have a use for for this. //Enable progress and configure the progress function worker.WorkerReportsProgress = true; worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged); */ // Configure the function to run when completed worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); // Launch the worker worker.RunWorkerAsync(); } /// <summary> /// This is the function that is called when the worker is launched with the RunWorkerAsync() call. /// </summary> /// <param name="sender">The worker as Object, but it can be cast to a worker.</param> /// <param name="e">The DoWorkEventArgs object.</param> void worker_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; for (int i = _Min; i < _Max; i++) { Value++; Thread.Sleep(1000); } } /// <summary> /// This worker_ProgressChanged function is not in use for this project. Thanks to INotifyPropertyChanged, this is /// completely unnecessary. /// </summary> /// <param name="sender">The worker as Object, but it can be cast to a worker.</param> /// <param name="e">The ProgressChangedEventArgs object.</param> void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) { // Does nothing yet throw new NotImplementedException(); } /// <summary> /// This worker_RunWorkerCompleted is called when the worker is finished. /// </summary> /// <param name="sender">The worker as Object, but it can be cast to a worker.</param> /// <param name="e">The RunWorkerCompletedEventArgs object.</param> void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { IsInProgress = false; } /// <summary> /// This function resets the Value of the counter to 0. /// </summary> private void Reset() { Value = Min; } #endregion } }
I’m sorry that this is not the most Newbie proof post. But I tried to comment like crazy the code so you can get through it.
Now if you find a discrepancy in my walk-through, please comment. Also, if it is easier for you to just download the project, here it is:
WPFProgressBarUsingBackgroundWorker.zip
Databinding in WPF allows binding the Command property to methods encapulated in an ICommand. By creating an ICommand object to hold an event function, the Command value can bind to the event function as an ICommand.
The goal of Model-View-ViewModel is to have zero code in the code behind of a WPF Control Instead, everything the WPF Control does happens using databinding.
While this article will show you how to do this, you be left a little fuzzy as to understanding of the implementation. It may take some time and research to fully understand everything this is doing. Understand that methods can be objects, and this is a process to turn a method object into an ICommand so it can be using in WPF for databinding.
You should have Visual Studio 2008/2010.
In Visual Studio, create a new WPF Application project and give it a name.
using System; using System.Windows.Input; namespace WpfDataBindingToICommand { public class RelayCommand : ICommand { #region Constructors public RelayCommand() { } #endregion } }
#region ICommand Members public bool CanExecute(object parameter) { throw new NotImplementedException(); } public event EventHandler CanExecuteChanged; public void Execute(object parameter) { throw new NotImplementedException(); } #endregion
#region Member Variables readonly Action<object> _ActionToExecute; readonly Predicate<object> __ActionCanExecute; #endregion
public bool CanExecute(object parameter) { return __ActionCanExecute== null ? true : __ActionCanExecute(parameter); }
public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } }
public void Execute(object parameter) { _ActionToExecute(parameter); }
The final class looks as follows:
using System; using System.Windows.Input; namespace WpfDataBindingToICommand { /// <summary> /// This RelayCommand object is used to encapsulate function logic into an oject that inherits ICommand. /// </summary> public class RelayCommand : ICommand { #region Member Variables readonly Action<object> _ActionToExecute; readonly Predicate<object> _ActionCanExecute; #endregion #region Constructors /// <summary> /// This creates a new RelayCommand. /// </summary> /// <param name="inActionToExecute">This is the logic of the actin to execute. This objects is usually a method that returns void.</param> public RelayCommand(Action<object> inActionToExecute) : this(inActionToExecute, null) { } /// <summary> /// This creates a new RelayCommand. /// </summary> /// <param name="inActionToExecute">This is the logic of the actin to execute. This objects is usually a method that returns void.</param> /// <param name="inActionCanExecute">This is the logic for whether the action can execute.</param> public RelayCommand(Action<object> inActionToExecute, Predicate<object> inActionCanExecute) { if (inActionToExecute == null) throw new ArgumentNullException("execute"); _ActionToExecute = inActionToExecute; _ActionCanExecute = inActionCanExecute; } #endregion #region ICommand Members public bool CanExecute(object parameter) { return _ActionCanExecute == null ? true : _ActionCanExecute(parameter); } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public void Execute(object parameter) { _ActionToExecute(parameter); } #endregion } }
Step 2 – Creating a ViewModelBase abstract base class
This object is used to create common logic for all objects that will be using in Binding. This object will implement INotifyPropertyChanged so it only has to be implemented once.
#region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; #endregion
#region Functions protected void NotifyPropertyChanged(String inPropertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(inPropertyName)); } } #endregion
The final object looks as follows:
using System; using System.ComponentModel; namespace WpfDataBindingToICommand { public abstract class ViewModelBase : INotifyPropertyChanged { #region Constructors public ViewModelBase() { } #endregion #region Functions protected void NotifyPropertyChanged(String inPropertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(inPropertyName)); } } #endregion #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; #endregion } }
Note: You may also want to implement IDisposable.
We are going to have the ViewModel and business in the same object for this example, but sometimes you will have a separate ViewModel object that represents your data/business.
using System; using System.Windows; using System.Windows.Input; namespace WpfDataBindingToICommand { public class SampleViewModel : ViewModelBase { #region Constructors public SampleViewModel() { } #endregion } }
public class SampleViewModel : ViewModelBase { string _Message = "Hello. This is the default message."; public string Message { get { return _Message; } set { _Message = value; NotifyPropertyChanged("Message"); } } }
public void ShowMessage(String inMessage) { MessageBox.Show(inMessage); }
RelayCommand _ShowMessageCommand; public ICommand ShowMessageCommand { get { if (_ShowMessageCommand == null) { _ShowMessageCommand = new RelayCommand(param => this.ShowMessage(Message)); } return _ShowMessageCommand; } }
Note: Notice that in order to pass the ShowMessage method, instead of the return value of the function, into the RelayCommand objectwhich is void anyway, the param => syntax is used.
The final SampleViewModel looks as follows.
using System; using System.Windows; using System.Windows.Input; namespace WpfDataBindingToICommand { public class SampleViewModel : ViewModelBase { #region Member Variables string _Message = "Hello. This is the default message."; RelayCommand _ShowMessageCommand; #endregion #region Constructors public SampleViewModel() { } #endregion #region Properties public string Message { get { return _Message; } set { _Message = value; NotifyPropertyChanged("Message"); } } public ICommand ShowMessageCommand { get { if (_ShowMessageCommand == null) { _ShowMessageCommand = new RelayCommand(param => this.ShowMessage(Message)); } return _ShowMessageCommand; } } #endregion #region Functions public void ShowMessage(String inMessage) { MessageBox.Show(inMessage); } #endregion #region Enums #endregion } }
Ok, so lets modify the XAML of the default MainWindow.xaml code that was auto-created with the project. We will keep it simple and have a text box and a button to pop up the message.
Note: For this simple program all the work we did to implement databinding for binding events to methods seems like an absurd burden. However, for large applications, this design will lead to a better way to manage your code. It will decouple your GUI from your code, making future refactoring of the GUI much easier. This also improves the ability to make minor changes to the GUI. It also makes the code more sustainable and more easily tested. Unit tests are more effective as the GUI layer is not required and most functions are in the business layer.
<window x:Class="WpfDataBindingToICommand.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfDataBindingToICommand" Title="MainWindow" Height="350" Width="525">
<window.Resources> <local:SampleViewModel x:Key="Sample" /> </window.Resources>
<grid.RowDefinitions> <rowDefinition Height="*" /> <rowDefinition Height="50" /> </grid.RowDefinitions>
<grid DataContext="{StaticResource ResourceKey=Sample}">
<textBox Text="{Binding Message}" Name="textBoxMessage" Margin="5"/>
<button Command="{Binding ShowMessageCommand}" Content="ShowMessage" Grid.Row="1" Height="23" Name="buttonShowMessage" HorizontalAlignment="Right" Width="Auto" Margin="5"/>
You are done. The final XAML is as follows:
<window x:Class="WpfDataBindingToICommand.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfDataBindingToICommand" Title="MainWindow" Height="350" Width="525"> <window.Resources> <local:SampleViewModel x:Key="Sample" /> </window.Resources> <grid DataContext="{StaticResource ResourceKey=Sample}"> <grid.RowDefinitions> <rowDefinition Height="*" /> <rowDefinition Height="50" /> </grid.RowDefinitions> <textBox Text="{Binding Message}" Name="textBoxMessage" Margin="5"/> <button Command="{Binding ShowMessageCommand}" Content="ShowMessage" Grid.Row="1" Height="23" Name="buttonShowMessage" HorizontalAlignment="Right" Width="Auto" Margin="5"/> </grid> </window>
Notice that we never touched the code behind of MainWindow. The GUI and the code are as decoupled as possible. Not event the event functions are needed in the code behind. This decoupling or GUI and code is our goal.
WPF Apps With The Model-View-ViewModel Design Pattern
Understanding Routed Events and Commands In WPF
Copyright ® Rhyous.com – Linking to this page is allowed without permission and as many as ten lines of this page can be used along with this link. Any other use of this page is allowed only by permission of Rhyous.com.
You have your WPF Window and you have an object that you don’t want to make a static resource. You want to declare it as a member variable in the code.
using System; using System.ComponentModel; namespace WPFPerson { public class Person : INotifyPropertyChanged { #region Member Variables String _FirstName; String _LastName; #endregion #region Constructors /* * The default constructor */ public Person() { } #endregion #region Properties public String FirstName { get { return _FirstName; } set { _FirstName = value; NotifyPropertyChanged("FirstName"); } } public String LastName { get { return _LastName; } set { _LastName = value; NotifyPropertyChanged("LastName"); } } #endregion #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(String info) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); } } #endregion } }
MainWindow.xaml (WPF Window)
<window x:Class="WPFPerson.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 Name="PersonGrid" > <textBox Height="23" HorizontalAlignment="Left" Margin="173,87,0,0" Name="textBoxFirstName" VerticalAlignment="Top" Width="234" Text="{Binding FirstName, Mode=TwoWay}" /> <textBox Height="23" HorizontalAlignment="Left" Margin="173,116,0,0" Name="textBoxLastName" VerticalAlignment="Top" Width="234" Text="{Binding LastName, Mode=TwoWay}"/> <label Content="FirstName" Height="28" HorizontalAlignment="Left" Margin="103,85,0,0" Name="labelFirstName" VerticalAlignment="Top" /> <label Content="LastName" Height="28" HorizontalAlignment="Left" Margin="103,114,0,0" Name="labelLastName" VerticalAlignment="Top" /> <button Content="Defaults" Height="23" HorizontalAlignment="Left" Margin="337,199,0,0" Name="buttonDefaults" VerticalAlignment="Top" Width="75" Click="buttonDefaults_Click" /> </grid> </window>
MainWindow.cs (Code Behind)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Threading; namespace WPFPerson { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { private readonly Person _Person; public MainWindow() { _Person = new Person(); InitializeComponent(); textBoxFirstName.DataContext = _Person; textBoxLastName.DataContext = _Person; } private void buttonDefaults_Click(object sender, RoutedEventArgs e) { _Person.FirstName = "Jared"; _Person.LastName = "Barneck"; } } }
Example 2 – Forthcoming…
Example 3 – Forthcoming…
Sources:
http://www.wrox.com/WileyCDA/Section/Windows-Presentation-Foundation-WPF-Data-Binding-with-C-2005.id-305562.html
Ok, so if you are going to have a string visible in your WPF application and your application can be in multiple languages, you are facing the localization problem.
Usually people as themselves two questions:
The answers are usually not now and I don’t know. So no localization work is done at first. Later, you wish you were more prepared for localization.
Well, I am here to tell you that you can at least prepare to be localized by doing a few simple steps:
If you are going to have a string in your WPF application, it is a good idea to store those strings in a centralized place for localization purposes. Usually in Visual Studio, that is in Resources.resx.
Often a string is entered directly into an the WPF XAML. This is not recommended. Maybe you are thinking that you don’t need to localize your application, so this is not important to you. Ok, really what you are thinking is:
“I don’t know how to do it and if I ever get big enough to need localization, at that point, I will figure it out.”
Well, what if I told you that using Resources.resx is extremely easy?
What if I told you that it hardly takes more time at all?
If it easy and hardly time consuming at all, you would do it, right? I would. Hence this post.
I have a project called LicenseAgreementManager. Right now this only needs to display a license agreement in English, but maybe someday, this will need to display a license agreement in any language.
In Visual Studio, create a new WPF Applcation project.
I named my project LicenseAgreementManager.
Right away, you already have at least one string statically entered into your XAML, the text for the window title.
You now have a publicized Resource.resx file and a few strings inside it.
<window x:Class="LicenseAgreementManager.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> </grid> </window>
<window x:Class="LicenseAgreementManager.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:p="clr-namespace:LicenseAgreementManager.Properties" Title="MainWindow" Height="350" Width="525">
<window x:Class="LicenseAgreementManager.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:p="clr-namespace:LicenseAgreementManager.Properties" Title="{x:Static p:Resources.EULA_Title}" Height="350" Width="525">
That was pretty easy, wasn’t it.
As you add elements that have strings, use the Resources.resx.
I have just added some items and removed the sizing as best as possible. Here is my XAML.
<window x:Class="LicenseAgreementManager.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:p="clr-namespace:LicenseAgreementManager.Properties" Title="{x:Static p:Resources.EULA_Title}" SizeToContent="WidthAndHeight" xml:lang="en-US"> <grid> <grid.RowDefinitions> <rowDefinition Height="Auto"/> <rowDefinition Height="Auto"/> </grid.RowDefinitions> <richTextBox Name="_EulaTextBox" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/> <stackPanel Grid.Row="1" Margin="0,10,0,0" Name="stackPanel2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <radioButton Content="{x:Static p:Resources.EULA_Accept}" Margin="20,20,20,0" Name="radioButton1" /> <radioButton Content="{x:Static p:Resources.EULA_NotAccept}" Margin="20,20,20,0" Name="radioButton2" /> <button Content="{x:Static p:Resources.Next_Button}" Name="button1" Margin="20,20,35,20" HorizontalAlignment="Right" /> </stackPanel> </grid> </window>
You don’t have to be localized to be prepared for easy localization. By doing the above simple steps, when it comes time to add localization, you will be ready.
If you want to go on an finish localization. You might want to read some of my sources.
Sources:
http://compositeextensions.codeplex.com/Thread/View.aspx?ThreadId=52910
http://msdn.microsoft.com/en-us/library/ms788718%28v=VS.90%29.aspx
http://msdn.microsoft.com/en-us/library/ms746621.aspx
Copyright ® Rhyous.com – Linking to this article is allowed without permission and as many as ten lines of this article can be used along with this link. Any other use of this article is allowed only by permission of Rhyous.com.
The XAML allows you to provide what are called StaticResources. Such resources can be given to a Window or to certain controls.
For this tutorial, I assume you are in Visual Studio 2008. I assume that you already know how to create a new Project and choose WPF Application. All examples assume you have a new WPF Application.
So lets get started with three examples of binding to StaticResources.
This example will demonstrate instantiating a String
as a StaticResource and binding a TextBox
to it.
TextBox
elements into the default Grid
control.
<listBox Margin="12,12,0,0" Name="listBox1" Height="100" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" /> <listBox Margin="138,12,20,0" Name="listBox2" Height="100" VerticalAlignment="Top" /> <textBox Margin="12,118,0,121" Name="textBox1" Width="120" IsReadOnly="True" HorizontalAlignment="Left" /> <textBox Margin="138,118,20,121" Name="textBox2" Width="120" IsReadOnly="True" HorizontalAlignment="Left" />
xmlns
reference to the System
namespace. This is done by adding the xmlns:System
line to as an attribute to the top Window
element as shown:
<window x:Class="StaticResourceBinding.Window1" 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" Title="Window1" Height="300" Width="300">
Strings
to it as StaticResources.
<window.Resources> <system:String x:Key="FirstName">Jared</system:String> <system:String x:Key="LastName">Barneck</system:String> <system:String x:Key="Alias">Rhyous</system:String> </window.Resources>
TextBox
elements to bind to each String added as a StaticResource by adding a Text attribute.
<textBox Text="{StaticResource FirstName}" Height="23" Margin="51,25,107,0" Name="textBox1" VerticalAlignment="Top" /> <textBox Text="{StaticResource LastName}" Height="23" Margin="51,54,107,0" Name="textBox2" VerticalAlignment="Top" /> <textBox Text="{StaticResource Alias}" Height="23" Margin="51,83,107,0" Name="textBox3" VerticalAlignment="Top" />
The final XAML looks as follows. No changes were made to the code behind at all.
<window x:Class="StaticResourceBinding.Window1" 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" Title="Window1" Height="300" Width="300"> <window.Resources> <system:String x:Key="FirstName">Jared</system:String> <system:String x:Key="LastName">Barneck</system:String> <system:String x:Key="Alias">Rhyous</system:String> </window.Resources> <grid> <textBox Height="23" Margin="51,25,107,0" Name="textBox1" VerticalAlignment="Top" Text="{StaticResource FirstName}"/> <textBox Height="23" Margin="51,54,107,0" Name="textBox2" VerticalAlignment="Top" Text="{StaticResource LastName}"/> <textBox Height="23" Margin="51,83,107,0" Name="textBox3" VerticalAlignment="Top" Text="{StaticResource Alias}"/> </grid> </window>
This example will demonstrate instantiating arrays
as StaticResources and binding a ListBox
to the arrays.
To show an example of building onto existing or previous learned knowledge, we are going to also implement binding each TextBox's
Text properties to the ListBox's
SelectedItem property.
ListBox
and two TextBox
elements into the default Grid
control.
<listBox Margin="12,12,0,0" Name="listBox1" Height="100" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" /> <listBox Margin="138,12,20,0" Name="listBox2" Height="100" VerticalAlignment="Top" /> <textBox Margin="12,118,0,121" Name="textBox1" Width="120" IsReadOnly="True" HorizontalAlignment="Left" /> <textBox Margin="138,118,20,121" Name="textBox2" Width="120" IsReadOnly="True" HorizontalAlignment="Left" />
xmlns
reference to the System
namespace. This is done by adding the xmlns:System
line to as an attribute to the top Window
element as shown:
<window x:Class="StaticResourceBinding.Window1" 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" Title="Window1" Height="300" Width="300">
<window.Resources> <x:Array x:Key="StringList" Type="System:String"> <system:String>Line 1</system:String> <system:String>Line 2</system:String> <system:String>Line 3</system:String> <system:String>Line 4</system:String> </x:Array> <x:Array x:Key="IntArray" Type="System:Int32"> <system:Int32>100</system:Int32> <system:Int32>200</system:Int32> <system:Int32>300</system:Int32> <system:Int32>400</system:Int32> </x:Array> </window.Resources>
ListBox's Text
property to bind to the String
array and the other ListBox's Text
property to bind to the Int32
array.
<listBox ItemsSource="{StaticResource StringList}" Margin="12,12,0,0" Name="listBox1" Height="100" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" /> <listBox ItemsSource="{StaticResource IntArray}" Margin="138,12,20,0" Name="listBox2" Height="100" VerticalAlignment="Top" />
TextBox
to the listBox1.SelectedItem
property and bind the other to the listBox2.SelectedItem
.
<textBox Text="{Binding ElementName=listBox1, Path=SelectedItem}" Margin="12,118,0,121" Name="textBox1" Width="120" IsReadOnly="True" HorizontalAlignment="Left" /> <textBox Text="{Binding ElementName=listBox2, Path=SelectedItem}" Margin="138,118,20,121" Name="textBox2" Width="120" IsReadOnly="True" HorizontalAlignment="Left" />
The final XAML looks as follows. No changes were made to the code behind at all.
<window x:Class="StaticResourceBinding2.Window1" 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" Title="Window1" Height="300" Width="300"> <window.Resources> <x:Array x:Key="StringList" Type="System:String"> <system:String>Line 1</system:String> <system:String>Line 2</system:String> <system:String>Line 3</system:String> <system:String>Line 4</system:String> </x:Array> <x:Array x:Key="IntArray" Type="System:Int32"> <system:Int32>100</system:Int32> <system:Int32>200</system:Int32> <system:Int32>300</system:Int32> <system:Int32>400</system:Int32> </x:Array> </window.Resources> <grid> <listBox ItemsSource="{StaticResource StringList}" Margin="12,12,0,0" Name="listBox1" Height="100" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" /> <listBox ItemsSource="{StaticResource IntArray}" Margin="138,12,20,0" Name="listBox2" Height="100" VerticalAlignment="Top" /> <textBox Text="{Binding ElementName=listBox1, Path=SelectedItem}" Margin="12,118,0,121" Name="textBox1" Width="120" IsReadOnly="True" HorizontalAlignment="Left" /> <textBox Text="{Binding ElementName=listBox2, Path=SelectedItem}" Margin="138,118,20,121" Name="textBox2" Width="120" IsReadOnly="True" HorizontalAlignment="Left" /> </grid> </window>
In the previous two examples, we added the resources to the main Window object. However, a resource can be added to a control.
This example will demonstrate instantiating an array
as a StaticResources for a control. We will then bind a TabControl’s ItemSource property to this array. This will cause a Tab to be created for each item in the array.
TabControl
into the default Grid
control.
<tabControl Name="tabControl1">
xmlns
reference to the System
namespace. This is done by adding the xmlns:System
line to as an attribute to the top Window
element as shown:
<window x:Class="StaticResourceBinding.Window1" 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" Title="Window1" Height="300" Width="300">
Grid.Resources
section in the XAML and add an array as a StaticResource under the Grid
control.
<grid.Resources> <x:Array x:Key="TabList" Type="System:String"> <system:String>Tab 1</system:String> <system:String>Tab 2</system:String> <system:String>Tab 3</system:String> </x:Array> </grid.Resources>
TabControl's ItemSource
property to bind to the String
array.
<tabControl Name="tabControl1" ItemsSource="{StaticResource TabList}">
The final XAML looks as follows. No changes were made to the code behind at all.
<window x:Class="StaticResourceBinding3.Window1" 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" Title="Window1" Height="300" Width="300"> <grid> <grid.Resources> <x:Array x:Key="TabList" Type="System:String"> <system:String>Tab 1</system:String> <system:String>Tab 2</system:String> <system:String>Tab 3</system:String> </x:Array> </grid.Resources> <tabControl Name="tabControl1" ItemsSource="{StaticResource TabList}"> </tabControl> </grid> </window>
Hey, there is nothing wrong with more examples, so if you have an example of your own feel free to add it as a comment.
Copyright ® Rhyous.com – Linking to this post is allowed without permission and as many as ten lines of this page can be used along with this link. Any other use of this page is allowed only by permission of Rhyous.com.
Today I needed a progress bar for as I was creating a c# tool that FTPs a file. I wanted to show progress as the FTP file was uploaded.
I found a good solution at Mikes Code Blog. He has a post and a follow-up post.
I created the progress bar as described and it worked really well. Here is a screen shot:
Thanks Mike!
The properties of WPF elements can be bound to properties of other WPF Elements. Lets do some simple examples of binding one element to another.
For this tutorial, I assume you are in Visual Studio 2008. I assume that you already know how to create a new Project and choose WPF Application. All examples assume you have a new WPF Application.
I am the believer that one example isn’t enough, so I am going to give you three examples:
This example will demonstrate binding a Button
‘s IsEnabled
property to a CheckBox
‘s IsChecked
property.
CheckBox
Button
The Button
is named button1
and the CheckBox
is named checkBox1.
checkBox1
to “Enable button”. This can be done either in the Properties or in the XAML.button1
element.button1
element:IsEnabled="{Binding ElementName=checkBox1, Path=IsChecked}"
In your project, ElementName
could be any item. In this example, we only have two elements so far: button1
, and checkBox1
.The XAML now looks like this (only two new lines exist):
<Window x:Class="BindingATextBoxToASlider.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <Button Content="Button" Height="23" Margin="12,34,416,0" Name="button1" VerticalAlignment="Top" Width="75" IsEnabled="{Binding ElementName=checkBox1, Path=IsChecked}"/> <CheckBox Content="CheckBox" Height="16" Margin="12,12,408,0" Name="checkBox1" VerticalAlignment="Top" /> </Grid> </Window>
Ok, so that was pretty cool. We have a simple example of binding one Element to another.
Yes, you can shoot yourself in the foot by doing something stupid.
You could bind an element to itself. Let’s try it just so you can see it happen.
Add the same binding you added to button1
to checkBox1
.
Compile and see what happens.
This example uses a Slider
and a TextBox
.
TextBox
Slider
The Slider
is named slider1
and the TextBox
is named textBox1.
textBox1
element.textBox1
element:Text="{Binding ElementName=slider1, Path=Value}"
ElementName
can be any item. In this example, we only have two elements so far: slider1
, and textBox1
.The XAML now looks like this (only two new lines exist):
<Window x:Class="BindingATextBoxToASlider.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <TextBox Height="23" Margin="79,62,99,0" Name="textBox1" VerticalAlignment="Top" Text="{Binding ElementName=slider1, Path=Value}"/> <Slider Height="22" Margin="79,34,99,0" Name="slider1" VerticalAlignment="Top" /> </Grid> </Window>
Ok, so maybe you want to try to do calculations in the XAML Binding. It doesn’t work.
You can enter this and while it will compile, the Binding won’t work:
Text="{Binding ElementName=slider1, Path=(int)Value}"
You can enter this and while it will compile, the Binding won’t work:
Text="{Binding ElementName=slider1, Path=Value + 1}"
Ok, lets do a slight more complex example. We are going to have more than two elements. We are going to have a ListBox
that contains a list of items (ListBoxItems
). We are going to have a TextBox
that displays the content of the selected item.
TextBox
ListBox
listBox1
. This can be done either in the XAML or by clicking on the button for Items
in the Properties of the listBox1
.textBox1
element.textBox1
element:Text="{Binding ElementName=listBox1, Path=SelectedItem.Content}"
Notice that we are using a property of a property for the Path. This is allowed. SelectedItem
is a property of listBox1
, and Content
is a property of SelectedItem
.The XAML now looks like this (only two new lines exist):
<Window x:Class="BindingATextBoxToAListBoxSelectedItem.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <TextBox Height="23" Margin="12,23,12,0" Name="textBox1" VerticalAlignment="Top" Text="{Binding ElementName=listBox1, Path=SelectedItem.Content}"/> <ListBox Margin="12,52,12,110" Name="listBox1"> <ListBoxItem>c:</ListBoxItem> <ListBoxItem>d:</ListBoxItem> <ListBoxItem>e:</ListBoxItem> <ListBoxItem>f:</ListBoxItem> <ListBoxItem>g:</ListBoxItem> <ListBoxItem>h:</ListBoxItem> </ListBox> </Grid> </Window>
Copyright ® Rhyous.com – Linking to this post is allowed without permission and as many as ten lines of this page can be used along with this link. Any other use of this page is allowed only by permission of Rhyous.com.
This document assumes that you understand the concepts of object oriented programming and more specifically with C# programming, such as Classes
or Objects
, Methods
, Properties
, Events
, etc. If not, it will be much harder to follow along.
This tutorial will cover the basics of data Binding
in a WPF application. When you are done with this tutorial, you should be able to create a basic WPF-based graphical program that uses Binding
. We will cover the different types of data Binding
as well as what works and sometimes what doesn’t.
The idea of data Binding
is to link a variable of any Type
(int
, string
, object
, etc…) to a graphical object’s Property that has the same type.
For example, lets say you have a Button
object called myButton
in your GUI like this: . The words “Click Me!” is a string property in the Button
object: myButton.Text
.
Imagine you have a string variable called strProperty
in some part of your code that on its own has no way to interact with your GUI code. Lets say you want to change the myButton.Text
property to match that string variable. Binding
allows the button’s text string to always match a string property in some other object not really related to your GUI so if you change strProperty to equal “Enable” your button text will look like . If you then change the strProperty to “Disable” the button text will automatically change to be without out your back end code having to make any interaction with the GUI on its own.
Without Binding
, you would have to write code yourself that would interact with the GUI and update the myButton.Text
property when ever you update the string in code. In order to do this without Binding
, you would also have to intermingle your background code with your GUI code. This can make it difficult to update or modify your GUI because GUI code is strung throughout all parts of your application. You don’t just have to update your GUI, you have to update all your code that interacts with the GUI.
So Binding
allows you to have a back end code that is independent of the GUI. This is especially useful when the GUI needs to be updated or improved or when multiple GUIs exists (skins) and you can switch between them.
There are programming styles associated with developing a GUI separate from the back-end. Two of which are Model-View-Control (MVC) or Model-View-ViewModel (MVVM). This tutorial is not going to cover these, however, it is probably wise for you become familiar with these.
However, there is no reason you are limited to Binding
to back end code. You can bind to code that is in the WPF GUI and very powerful applications can be written with little to no back end code.
In order to using data Binding, you should have the following requirements:
System.ComponentModel.INotifyPropertyChanged
.There is are multiple types of Binding
. Elements bind to some type of resource and there are multiple types of resources. Static and Dynamic resource binding which uses the StaticResource Markup Extension or the DynamicResource Markup Extension.
The Binding
source can also be “any public property, including properties of other controls, common language runtime (CLR) objects, XAML elements, ADO.NET DataSets, XML Fragments, and so forth.” (Reference: http://msdn.microsoft.com/en-us/magazine/cc163299.aspx).
Go to next: 1.1 Binding one element property to another
Copyright ® Rhyous.com – Linking to this page is allowed without permission and as many as ten lines of this page can be used along with this link. Any other use of this page is allowed only by permission of Rhyous.com.