Category Archives: WPF

Data Binding (part 1, advanced), WPF

I was studying the “Google Authentication” as I plan to write a post about it. But then I came across the WPF data binding, which I was avoiding so far, because code pieces are not easy to display in posts due to their complexity. The “Google Authentication” source code will use WPF data binding. It therefore makes sense to post about WPF data binding first. Newbies have problems with WPF. It can be quite overwhelming at the beginning. This post is not covering the basics. I expect them to be known already. I will only concentrate on some data binding with TextBoxes today.

Have a look at the XAML:

<Window x:Class="WpfDatabinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
        Title="MainWindow" Height="194" Width="374">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <Label Grid.Column="0" Grid.Row="0" Content="Value1, Init only" VerticalAlignment="Center" />
        <TextBox Grid.Column=" 1" Grid.Row="0" VerticalAlignment="Center" Text="{Binding Value1, Mode=OneTime}" />

        <Label Grid.Column="0" Grid.Row="1" Content="Value1, OneWay" VerticalAlignment="Center" />
        <TextBox Grid.Column=" 1" Grid.Row="1" VerticalAlignment="Center" Text="{Binding Value1, Mode=OneWay}" />        

        <Label Grid.Column="0" Grid.Row="2" Content="Value2, OneWayToSource" VerticalAlignment="Center" />
        <TextBox Grid.Column=" 1" Grid.Row="2" Text="{Binding Value2, Mode=OneWayToSource}" VerticalAlignment="Center" />

        <Label Grid.Column="0" Grid.Row="3" Content="Value3, OneWay" VerticalAlignment="Center" />
        <TextBox Grid.Column=" 1" Grid.Row="3" Text="{Binding Value3, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" />
        
        <Label Grid.Column="0" Grid.Row="4" Content="Value3, TwoWay" VerticalAlignment="Center" />
        <TextBox Grid.Column=" 1" Grid.Row="4" Text="{Binding Value3, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" />

        <Label Grid.Column="0" Grid.Row="5" Content="Value3, TwoWay" VerticalAlignment="Center" />
        <TextBox Grid.Column=" 1" Grid.Row="5" Text="{Binding Value3, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" />
    </Grid>
</Window>

Instead of assigning simple text literals to the TextBoxes like: Text=”Hello World!”, we assign binding definitions in curly brackets. For instance: Text=”{Binding Value2, Mode=OneWayToSource}”
In this case we connect the property “Value2” with the TextBox text. The Mode tells the binding direction:

Mode

The UpdateSourceTrigger=PropertyChanged implies that the binding source class has implemented the interface INotifyPropertyChanged, which requires the event implementation called PropertyChanged. When the program raises this event with the correct property name, which is also defined in the XAML file, then the binding will refresh the GUI element.

And here is the program. Create a BindingClass instance and assign it to the current window DataContext. There is nothing else to do. The rest is taken care of by the .Net Framework and compiler. WPF is quite abstract. A lot of code is generated out of the XAML files. You cannot see the generated code without pressing the icon “Show All Files” in the Solution Explorer. What is important to know is that we are mainly looking at partial files (here: “public partial class MainWindow : Window”). The rest is auto-generated source code.

using System.Windows;

namespace WpfDatabinding {
   public partial class MainWindow : Window {

      public MainWindow() {
         InitializeComponent();
         DataContext = new BindingClass();
      } //

   } // class
} // namespace
using System;
using System.ComponentModel;
using System.Timers;

namespace WpfDatabinding {
   public class BindingClass : INotifyPropertyChanged {
      public event PropertyChangedEventHandler PropertyChanged;

      private string _Value1 = "Value1 XX:XX:XX";
      private string _Value2 = "Value2 XX:XX:XX";
      private string _Value3 = "Value3 XX:XX:XX";

      public string Value1 { get { return _Value1; } set { _Value1 = value; RaiseEvent("Value1"); } }
      public string Value2 { get { return _Value2; } set { _Value2 = value; RaiseEvent("Value2"); Console.WriteLine(_Value2); } } // event has no effect
      public string Value3 { get { return _Value3; } set { _Value3 = value; RaiseEvent("Value3"); } } // event has effect on TwoWay

      public BindingClass() {
         Timer lTimer = new Timer(1000.0);
         lTimer.Elapsed += new ElapsedEventHandler(Timer_Elapsed);
         lTimer.Start();
      } // constructor

      void Timer_Elapsed(object sender, ElapsedEventArgs e) {
         string lNow = DateTime.Now.ToString("HH:mm:ss");
         Value1 = "Value1 " + lNow;
      } // 

      private void RaiseEvent(string xPropertyName) {
         var lEvent = PropertyChanged;
         if (lEvent == null) return;
         lEvent(this, new PropertyChangedEventArgs(xPropertyName));
      } //

   } // class
} // namespace

The BindingClass is straight forward. There are no events that are triggered by the GUI. The separation of XAML and program code is a pretty cool approach.
There are three properties: Value1, Value2 and Value3.
Value1 is constantly written to by a timer event that is raised each second.
Value2 is only receiving values from the GUI, because the corresponding TextBox is using Mode=OneWayToSource. This does not print anything in the GUI, hence I added the Console.WriteLine(_Value2) statement. Check the output there. The statement RaiseEvent("Value2") has no impact, writing to a TextBox would be the wrong direction for this mode.
And RaiseEvent("Value3") only impacts TwoWay TextBoxes, but not on the OneWay TextBox.
Play with the textboxes and see what reacts on what. My hint is that the last three TextBoxes are the most interesting ones.

WpfDataBinding

example output on the console window:
What happens with Value 2 ?

Remoting (part 1, basics), old school

Before I come to the new WCF style Remoting, I would like to introduce the good old System.Runtime.Remoting namespace.
Remoting is a convenient way to call methods across the network. You don’t have to write messages to trigger methods on the server, wait for results and then analyse feedback messages on the client side. What could be easier than calling server methods directly?

The downside is the slowness and the missing encryption. Nevertheless, if you don’t have many server requests then Remoting is probably the right solution for you. The ease is hard to beat.

We need a library to share the syntax between the server and client. In practice you would compile the following code into a library and implement it on both sides. Don’t be lazy and only share the source code. This cannot work, because two compilers generate two different system object IDs.

In today’s example we are running the code in one Visual Studio process. We are also using the localhost. This is why we do not need an external library on both sides. We are using the same compiler object ID.
The class MarshalByRefObject enables Remoting access to objects across application domain boundaries. We need to inherit this class.

public abstract class RemotingShared : MarshalByRefObject {

   public const string RemotingName = "MyRemotingName";
   public const string ServerIpAddress = "127.0.0.1";
   public const int Port = 65432;


   [Serializable]
   public class TradeData {
      public DateTime tradeTime;
      public string ticker;
      public double price;
      public int quantity;
         
      public override string ToString() {
         return tradeTime.ToString("dd.MMM.yy HH:mm:ss ") + ticker + " " + quantity + " @ " + price + " EUR";
      } //

      public TradeData(DateTime xTradeTime, string xTicker, double xPrice, int xQuantity) {
         tradeTime = xTradeTime;
         ticker = xTicker;
         price = xPrice;
         quantity = xQuantity;
      } // constructor

   } // class

   public abstract List<TradeData> GetTrades();
   public abstract string HelloWorld(string xName);
   public abstract DateTime Ping();      
} // class

The server overrides methods of the abstract class RemotingShared by inheriting it. We do not need to create any instance. This is done in the background by the Remoting process. The option WellKnownObjectMode.Singleton makes sure that only one instance will be created and reused. WellKnownObjectMode.SingleCall would create new instances for each incoming message.

public class RemotingSharedDerived : RemotingShared {

   public override List<RemotingShared.TradeData> GetTrades() {
      DateTime lDummyTime = new DateTime(2014, 02, 12, 16, 30, 10) ;
      RemotingShared.TradeData lTrade1 = new TradeData(lDummyTime, "DTE", 11.83, 100);
      RemotingShared.TradeData lTrade2 = new TradeData(lDummyTime.AddSeconds(2), "DAI", 66.45, 300);
      RemotingShared.TradeData lTrade3 = new TradeData(lDummyTime.AddSeconds(5), "DBK", 35.91, 100);
      return new List<TradeData>() { lTrade1, lTrade2, lTrade3 };
   } //

   public override string HelloWorld(string xName) {
      return "Hello " + xName;
   } //

   public override DateTime Ping() { 
      return DateTime.Now; 
   } //
} // class

public static void StartServer() {
   TcpChannel lTcpChannel = new TcpChannel(RemotingShared.Port);
   ChannelServices.RegisterChannel(lTcpChannel, true);
   Type lRemotingSharedType = typeof(RemotingSharedDerived);
   RemotingConfiguration.ApplicationName = RemotingShared.RemotingName + "App";
   RemotingConfiguration.RegisterWellKnownServiceType(lRemotingSharedType, RemotingShared.RemotingName, WellKnownObjectMode.Singleton);         
} //

Let’s have a look at the client now. As usual I kept the code as short as possible. Personally I do not like example programs that include too much redundant information, they can be quite confusing sometimes.
As we are on the localhost, the server has registered a ‘tcp’ channel already. We check if it exists although we already know it does. But the example program would throw an exception otherwise. Keep it in the code, it does make sense when you are remoting between two different IP addresses.

public static void StartClient() {
   string lPath = "tcp://" + RemotingShared.ServerIpAddress + ":" + RemotingShared.Port + "/" + RemotingShared.RemotingName;
   TcpChannel lTcpChannel = new TcpChannel();

   if (!ChannelServices.RegisteredChannels.Any(lChannel => lChannel.ChannelName == lTcpChannel.ChannelName)) {
      ChannelServices.RegisterChannel(lTcpChannel, true);
   }

   RemotingShared lShared = (RemotingShared)Activator.GetObject(typeof(RemotingShared), lPath);
   var o = lShared.GetTrades();
   var s = lShared.HelloWorld("Mr. Ohta");
   var p = lShared.Ping();

   Console.WriteLine("GetTrades():");
   foreach (var lTrade in o) Console.WriteLine(lTrade.ToString());
   Console.WriteLine();
   Console.WriteLine("HelloWorld(): " + s);
   Console.WriteLine();
   Console.WriteLine("Ping(): " + p);
} //

Let’s run the code now.

public static void Test() {
   RemotingServer.StartServer();
   RemotingClient.StartClient();
   Console.WriteLine("\nPress any key to exit.");
   Console.ReadKey();
} //

example output:
GetTrades():
12.Feb.14 16:30:10 DTE 100 @ 11.83 EUR
12.Feb.14 16:30:12 DAI 300 @ 66.45 EUR
12.Feb.14 16:30:15 DBK 100 @ 35.91 EUR

HelloWorld(): Hello Mr. Ohta

Ping(): 12/02/2014 20:36:55

Press any key to exit.