Thursday, October 28, 2010

WPF - Style Selector for Items in ItemsControl

Using ItemsControl, we can display list of data in WPF view. It is, sometimes, a requirement that data with different values appear differently on the view so that user focus could be triggered on important information in the list. WPF supports this treatment of items in ItemsControl through Selector.

Selector is available in System.Windows.Controls namespace. In order to define custom selectors, we need to inherit from this and override SelectStyle method. This method provides you the item as an argument. We can look at different properties of the item and apply specific style to it.

Lets have a look at an example where we could use StyleSelector. In this example we are creating a Window with a ComboBox and ListBox. We have to show the same list of students in both controls. Those students which are not Enrolled in a particular program yet should be highlighted with red background. This would help the user to identify students and helping them with enrollment (if required).

The definition of View is as follows. This view is using WindowStyleSelectorDemonstratorViewModel as its DataContext. It is expecting that the DataContext has some collection which is bound to its ComboBox and ListBox. It is also defining StudentItemStyleSelector as a resource and using it as ItemContainerStyleSelector for ComboBox and ListBox.

<Window x:Class="WPFCommandLineArguments.WindowStyleSelectorDemonstrator"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFCommandLineArguments"
Title="WindowStyleSelector" Height="300" Width="566">
<Window.Resources>
<local:StudentItemStyleSelector x:Key="studentStyle" />
</Window.Resources>
<Window.DataContext>
<local:WindowStyleSelectorDemonstratorViewModel />
</Window.DataContext>
<Grid>
<Label Content="Students" Height="22" Margin="20,18,350,0"
Name="label1" VerticalAlignment="Top" Target="{Binding ElementName=comboStudents}" />
<ComboBox Height="21" Margin="20,46,0,0" Name="comboStudents"
VerticalAlignment="Top" HorizontalAlignment="Left" Width="174"
ItemsSource="{Binding Path=Students, Mode=OneWay}" DisplayMemberPath="StudentName"

SelectedValuePath="StudentId"
ItemContainerStyleSelector="{DynamicResource studentStyle}" />
<Label Content="Students" Height="22" Margin="247,18,123,0" Name="label2"
VerticalAlignment="Top" Target="{Binding ElementName=listBoxStudents}" />
<ListBox Height="195" HorizontalAlignment="Left" Margin="247,46,0,0"
Name="listBoxStudents" VerticalAlignment="Top" Width="246" ItemsSource="{Binding Students}"
ItemContainerStyleSelector="{DynamicResource studentStyle}" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding StudentName}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>

The code behind for WindowStyleSelectorDemonstrator is as follows:

public partial class WindowStyleSelectorDemonstrator : Window
{
public WindowStyleSelectorDemonstrator()
{
InitializeComponent();
}
}

As expected by the view, we create the view model WindowStyleSelectorDemonstratorViewModel. We define an ObservableCollection named Students which is used as ItemsSource by the ComboBox and ListBox in the View.

class WindowStyleSelectorDemonstratorViewModel : DependencyObject
{
public ObservableCollection Students
{
get { return (ObservableCollection) GetValue(StudentsProperty); }
private set { SetValue(StudentsProperty, value);}
}

public WindowStyleSelectorDemonstratorViewModel()
{
Students = new ObservableCollection();
Students.Add(new Student{StudentId = 1, StudentName = "Muhammad", IsEnrolled = true});
Students.Add(new Student { StudentId = 1, StudentName = "Jim", IsEnrolled = false });
Students.Add(new Student { StudentId = 1, StudentName = "Mark", IsEnrolled = false });
Students.Add(new Student { StudentId = 1, StudentName = "Peter", IsEnrolled = true });
Students.Add(new Student { StudentId = 1, StudentName = "Daisy", IsEnrolled = false });
Students.Add(new Student { StudentId = 1, StudentName = "Jennifer", IsEnrolled = true });
Students.Add(new Student { StudentId = 1, StudentName = "Erwin", IsEnrolled = true });
Students.Add(new Student { StudentId = 1, StudentName = "Mike", IsEnrolled = false });
Students.Add(new Student { StudentId = 1, StudentName = "Jason", IsEnrolled = true });
Students.Add(new Student { StudentId = 1, StudentName = "Asif", IsEnrolled = true });

}

public DependencyProperty StudentsProperty = DependencyProperty.Register("Students",
typeof (ObservableCollection),


typeof(WindowStyleSelectorDemonstratorViewModel));
}

Each item of the collection is of type Student. The expected properties are StudentName, StudentId and IsEnrolled. Lets define this as DependencyObject. We are registering each property as DependencyProperty so that WPF runtime could provide change notification for these properties.

class Student : DependencyObject
{
//Student Id
public static DependencyProperty StudentIdProperty =
DependencyProperty.Register("StudentId", typeof (int), typeof (Student));
public int StudentId
{
get { return (int) GetValue(StudentIdProperty); }
set { SetValue(StudentIdProperty, value);}
}

//Student Name
public static DependencyProperty StudentNameProperty =
DependencyProperty.Register("StudentName", typeof (string), typeof (Student));
public string StudentName
{
get { return (string) GetValue(StudentNameProperty); }
set {SetValue(StudentNameProperty, value);}
}

//IsEnrolled
public static DependencyProperty IsEnrolledProperty =
DependencyProperty.Register("IsEnrolled", typeof (bool), typeof (Student));
public bool IsEnrolled
{
get { return (bool) GetValue(IsEnrolledProperty); }
set {SetValue(IsEnrolledProperty, value);}
}
}

The most important part of the example is as below. Here we are defining a Selector for Student. Since it is based on ContentControl, we can use it for both ListBoxItem and ComboBoxItem. Had we needed setting any specialized property for ListBoxItem or ComboBoxItem then we would have defined different Selectors for each. In the Selector code below, we are looking at IsEnrolled property of Student. If IsEnrolled is false, we are setting the background color ContentControl to Red. We have used Brushes, from System.Windows.Media, to set the Background brush. It has common brushes defined as static properties.

class StudentItemStyleSelector : System.Windows.Controls.StyleSelector
{
public override Style SelectStyle(object item, DependencyObject container)
{
Style comboBoxItemStyle = new Style(typeof(Control));

Student student = (Student) item;

if (!student.IsEnrolled)
{
comboBoxItemStyle.Setters.Add(new Setter(Control.BackgroundProperty, Brushes.Red));
}
else
{
comboBoxItemStyle.Setters.Add(new Setter(Control.BackgroundProperty, Brushes.White));
}

return comboBoxItemStyle;
}
}


When we run the application, it should be displayed as follows (after opening the popup of combo box).

Wednesday, October 27, 2010

Timers for .net Applications

While writing applications for our customers, we find many different options to implement the same functionality. One such situation is when it comes to the use of timer. There are a multitude of timers available in .net. Which one should we use? There might be situations in which using any of them would make no difference but there would definitely be scenarios in which one would make sense to be used. There are many references on the internet discussing the details about each of them. I just want to put some light on their differences and what make them different. This could help you in choosing the best one for your situation.

When we set the Interval of these timers, on the back of our minds, sometimes, we think we want the specified piece of code to be executed after this certain time interval.

Generally, we find four timers used in .net applications. The first two are called Multithreaded timers and the last two are called Single Threaded Timers.

1. System.Timers.Timer
2. System.Threading.Timer
3. System.Windows.Forms.Timer
4. System.Windows.Threading.DispatcherTimer

Three of the four timers described above are named Timer.

System.Timers.Timer:
As MSDN suggests, it is provided to support Server based timer functionality in applications.

The callback is executed on the ThreadPool thread supplied by the system. If we need to use elements on the UI thread in the callback, we need to invoke them using Dispatcher.

In Windows Forms, there might be an alternative approach by using the SynchronizationObject property of this timer. In that case this would be raised on the thread the SynchronizingObject was created. If we set this to a UI object (Window, button or anything else) then this would be raised on UI thread. Basically, whenever the interval elapses, OS reports timer about it and timer calls BeginInvoke on the SynchronizingObject to execute the timer callback. This makes it functionally equivalent of using Single threaded timers [DispatcherTimer or Forms.Timer] as it is using message pumping but it uses that after being invoked from OS [which is different than single threaded timers]. So if UI thread is busy, it keeps on queuing up the callback on the dispatcher. When Dispatcher gets back, it executes them by picking them from the queue in the same order. This is possible for WindowsForms as Control implements ISynchroizeInvoke which can be assigned to SynchronizationObject . This definitely has limitations for software libraries as a hold of UI control is difficult. Keeping this in mind, .net 2.0 introduced SynchronizationContext. So a library might get a hold of SynchronizationContext.Current without being worried about holding a UI object's reference. Basically, it provides virtual methods Send and Post which are inherited by more specialized types for each environments [WindowsFormsSynchronizationContext for Windows Forms and DispatcherSynchronizationContext for WPF]. An instance of this specialized SynchronizationObject is created before the message loop is started. This is used for marshalling for classes like BackgroundWorker (through AsyncOperationManager). So, for WPF application, we can obtain the current instance of SynchronizationContext and post on UI thread. Alternatively, just invoking (Synch or ASynch) using Dispatcher can also be used.

http://msdn.microsoft.com/en-us/magazine/cc163600.aspx

If you still try to assign a WPF element to SynchronizingObject property of Timers.Timer, it results in an exception.



After the lapse of duration specified in Interval, this results in firing of Elapsed event once. If AutoReset is not set, this would not be raised again. By default It is set to true.

This implements a Component based model so we should directly add it to our toolbox. If we drop it on a Window then SynchronizingObject property of this timer should automatically be assigned to the Window. But for WPF, even in VS2010, we can not drag and drop it to the Window. In Windows Forms, if we drop it on a form, it automatically assigns the SynchronizingObject property to the current form. So based on this discussion, it is not very useful for WPF that it inherits from System.ComponentModel.Component.



As discussed above, you will find it disabled like this:



As above, there are other timers too, which implement the same model. We can add all these timers to the Visual Studio Designer toolbox.

System.Windows.Threading.DispatcherTimer:
It seems to be the same thing for WPF as System.Windows.Forms.Timer for Winform applications. The callback is executed on the Dispatcher Queue. Since it is executed on the UI thread, we don't need to use dispatcher to access elements on the UI thread. So no need to write code like this:

this.Dispatcher.BeginInvoke(...)

Being on Dispatcher queue, it is not guaranteed to be executed at the exact interval. If UI thread is busy in some blocking If it is a time critical or real time process, then probably, we should not be using it. It is specially suited for executing any UI behavior at certain interval which, if delayed, does not cause any problems.

This timer allows us to execute some callback after every certain duration. This duration is set by Interval property. The minimum interval allowed is 10 milliseconds.

System.Threading.Timer
This is the second multithreaded timer in .net. The other is System.Timers.Timer. This timer results in running the callback in a separate background thread so there is no event available for subscription. This is a ThreadPool thread provided by the system. This timer is the simplest of all timer with the mini

This is the only timer for which we can specify due time. Due time is basically the time when the timer should start its actual operation even if it is started. So we can have it set so that it starts raising elapsed events each 5 minutes (periodic Interval) but only after 4 hours starting from now. If we just want to execute it once then specify period as 0 (Zero) or Timeout.Infinite. If we want to start timer immediately then specify due time as 0.

System.Windows.Forms.Timer
Those who have come to WPF through Winform background might remember the ClockBox shaped timer control in the ToolBox for Winform designer in Visual Studio. This timer also run in UI thread. So all limitations of DispatcherTimer would be applicable for this timer as well.

As documented by Microsoft, this timer has an accuracy of 55 milliseconds. So, even if, we can set the interval less than 55 ms (upto 1 ms), we might have missed events for this timer.

http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx

MultiThreaded Timer callbacks / handlers after being stopped / disposed:
The callbacks / handlers of multithreaded timers [System.Timers.Timer and System.Threading.Timer] might get executed after them being disposed. This is because they both use ThreadPool timers and it is possible that they are disposed after their callbacks / handlers get queued. Similar logic applies when we stop a thread after its callback is already in ThreadPool Queue.

Following is the display when a timer callback is queued after elapsing the interval.


Now when the timer is stopped / disposed then this callback would still execute by a ThreadPool thread. This might be unexpected for any application. Now let us discuss how we can work around this.

System.Threading.Timer has this amazing overload of Dispose [Dispose(WaitHandle)]. You can find it here [http://msdn.microsoft.com/en-us/library/b97tkt95.aspx]. It waits disposing until all currently queued callbacks have completed execution. After disposing, it signals the WaitHandle passed as argument.

Simultaneous Execution of multiple callback / handlers for multithreaded timers:
Since multithreaded timers use ThreadPool thread, their handlers / callbacks are queued for execution as per the interval settings. It is possible that ThreadPool thread has more than one handlers for same timer in the queue and they are executed simultaneously. This situation should be specially considered as this might lead to an unexpected situation if we don't consider the possibility of re-entrance.



Single Threaded Timer outside Environment:
If we use a single threaded timer in an environment they are not supposed to be used [DispatcherTimer outside WPF or Windows.Forms.Timer outside Winform] then their timers wouldn't be raised. You can be happy with calling Start on the timer but their event handlers wouldn't ever get executed. You might test that by using Winform timer in a WPF application or any of these timers in a Windows Service. This actually makes sense as they use their respective Dispatcher and if one is not available then how can we expect one to work.

Updating Timer Interval:
Since most of these timers (except System.Threading.Timer) has an Interval property, we might want to change it sometime. It must be remembered that updating the interval property during the time when the timer is still running restarts the current interval. So e.g. Interval was set as 5000, the timer is running and 3000 milliseconds are already passed. Now we change the interval to 15000 milliseconds. It must be remembered that the next callback execution would be after 15000 milliseconds after this update. So in total the callback execution after previous one is after 18000 milliseconds.

Single Thread Timer events when UI thread is sleeping:
When UI thread is sleeping then single thread timer events are not generated. So if our UI thread is sleeping for 2 seconds and we have timer interval set as 400 ms then we would miss 5 events. We should be taken this into consideration when using single threaded timers. This is very unlikely scenario because no developer would be causing UI thread to sleep. But if you see missed events then you can consider this possibility.

Timers and Memory Leak:
Keeping external references would not let an object to be garbage collected. This causes memory leak. When we subscribe with Tick / Elapsed event, it seems that it is an internal reference so it shouldn't be causing a memory leak. But it mostly does. I said mostly because it does it for all single and multithreaded timers except System.Threading.Timer.

Basically, there is an external reference in the background. In order to fire the event for timer (Tick / Elapsed), .net framework holds the reference of the timer. This makes GC think that this object should not be collected when it is come across during Garbage Collection.

In order to fix this, we should be using the same universal remedy as we use for all event handlers. Just unsubscribe the timer event in Dispose(). As soon as the event handler is unsubscribed, the framework gets rid of timer reference hence no memory leak. The other solution would be to Dispose() the timer (as timers implement IDiposable themselves).

In the beginning of the memory leak discussion, I told you that MOST of the timers could cause a memory leak. The exception is System.Threading.Timer. As you can guess, there is no event that we subscribe to, in this case. We just need to provide a callback delegate which is run by the runtime after each Interval.

Other Timers:
There are other times too. Let us discuss them briefly:

1. System.Web.UI.Timer
This is used to perform web page postbacks at required intervals. When used as a trigger event for UpdatePanel in AJAX, this causes partial page postbacks of webpage. This is a server timer so you can have server based code in the Tick event. This presents a useful option for implementing financial applications to provide a mechanism to update the page based on certain interval. We can also use it in gaming / sports based web applications where we need to update the scorecards at regular intervals.

2. Multimedia Timer
Multimedia timers have accuracy down to 1 millisecond. This can prove to be the best real time timer that you could have. The issue is that this timer is not available in .net framework so you would have to use native interop for this timer.

You can read further about this timer here:
http://msdn.microsoft.com/en-us/library/system.web.ui.timer.aspx

Which Timer to use
Based on the above discussion, the general idea that you should keep in your mind can be summarized as follows:

1. If more accuracy is needed for the timing of firing the event, then use one of the Timers which do not use UI thread (System.Timers.Timer or System.Threading.Timer).

2. If you need to access elements on UI (like UI controls), it makes more sense to use UI related timers instead (DispatcherTimer or Forms.Timer). Otherwise you need to use Dispatcher to invoke operations on elements on UI thread.

3. If you are worried about re-entrancy of multiple callbacks handlers(simultaneous execution of multiple callbacks). Or if you don't want to handle the scenarios in which timer might be disposed / stopped before executing all waiting callbacks, it would make more sense to use single threaded timers.

Tuesday, October 19, 2010

WPF - Data Binding Cheat Sheet

As a developer, we are not supposed to rememeber so much as we think. If you are struggling with remembering all Data Binding syntax, then you should consider printing this and paste it on your office wall. Believe me, it helps a lot!

http://www.nbdtech.com/Blog/archive/2009/02/02/wpf-xaml-data-binding-cheat-sheet.aspx

Sunday, October 17, 2010

WPF - Expander in ItemsControl (ListBox)

ItemsControl uses VirtualizingStackPanel as Panel of its item. This induces virtual behavior for its item. The elements which are not visible will not be loaded. This provides good performance of WPF applications. The problem is that VirualizingStackPanel introduces issues for resizing when the size of ItemsControl is expected to be reduced because some items are deleted or the size is decreased because some items are collapsed. Because of VirtualizingStackPanel, ItemsControl maintains its size. This would occupy the same space even if items are collapsed which is kind of unexpected sometimes.

Let us see that in an example. Let's create a ListBox with Expander as ListBoxItems. To keep track of actual size of some of the controls in this example, we create TextBlocks to note down their sizes.
<StackPanel>
<TextBlock Text="{Binding ElementName=lstBox, Path=ActualHeight}" />
<TextBlock Text="{Binding ElementName=tst, Path=ActualHeight}" />
<TextBlock Text="{Binding ElementName=tstLBI, Path=ActualHeight}" />
<Border BorderThickness="1"
BorderBrush="Black"
Margin="5"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<ListBox x:Name="lstBox">
<ListBox.Items>
<ListBoxItem x:Name="tstLBI">
<Expander Header="Child1" x:Name="tst"
HorizontalAlignment="Left" Height="auto" Width="auto">
<Canvas Background="Blue" Height="50" Width="100" >
<Border Background="Blue"/>
</Canvas>
</Expander>
</ListBoxItem>
<Expander Header="Child2"
HorizontalAlignment="Left">
<Canvas Height="50"
Width="100"
Background="Blue" />
</Expander>
<Expander Header="Child3"
HorizontalAlignment="Left">
<Canvas Height="50"
Width="100"
Background="Blue" />
</Expander>
</ListBox.Items>
</ListBox>
</Border>
</StackPanel>



Now we expand the expander controls in the ListBox as follows:



Now we collapse all Expander controls. As you can see that ListBox still has the same size as all items are still expanded. You can verify that TextBlock is also showing ListBox size as 223.



This is because ListBox uses VirtualizingStackPanel as its ItemsPanel. If we change that to StackPanel as follows, we can fix this.
<StackPanel>
<TextBlock Text="{Binding ElementName=lstBox, Path=ActualHeight}" />
<TextBlock Text="{Binding ElementName=tst, Path=ActualHeight}" />
<TextBlock Text="{Binding ElementName=tstLBI, Path=ActualHeight}" />
<Border BorderThickness="1"
BorderBrush="Black"
Margin="5"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<ListBox x:Name="lstBox">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.Items>
<ListBoxItem x:Name="tstLBI">
<Expander Header="Child" x:Name="tst"
HorizontalAlignment="Left" Height="auto" Width="auto">
<Canvas Background="Blue" Height="50" Width="100" >
<Border Background="Blue"/>
</Canvas>
</Expander>
</ListBoxItem>
<Expander Header="Child"
HorizontalAlignment="Left">
<Canvas Height="50"
Width="100"
Background="Blue" />
</Expander>
<Expander Header="Child"
HorizontalAlignment="Left">
<Canvas Height="50"
Width="100"
Background="Blue" />
</Expander>
</ListBox.Items>
</ListBox>
</Border>
</StackPanel>

Now when you run the application, this issue should be fixed.

Sunday, October 10, 2010

WPF - UIElement's Opacity Mask

In this post, we will be discussing how we can apply opacity mask to WPF UIElement. First of, we need to discuss what is opacity mask. When we apply a brush as opacity mask to a UIElement, only alpha channel of the brush is used. All other channels of the brush are ignored. Using Opacity mask we can make portions of an element either transparent or semi transparent.

Using SolidColorBrush for Opacity of whole UIElement:

By using SolidColorBrush for opacity mask, we can control the opacity of whole UIElement. In the example below, we are specifying a SolidColorBrush for opacity mask of a Button.

<Window x:Class="WpfApplication3.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>
<Rectangle Fill="Gray" />
<Button Height="38" Margin="20,48,11,0" Name="button1" VerticalAlignment="Top" Content="Button">
<Button.OpacityMask>
<SolidColorBrush Color="#B2021233" />
</Button.OpacityMask>
</Button>
</Grid>
</Window>

When we run this, it shows as follows:



You can see that opacity mask has ignored the color of the brush. It only has used its alpha channel. If we change the alpha channel to FF, this would be completely opaque, if we change it to 00, it would completely transparent. In the brush used above the alpha channel is specified as:


Using LinearGradientBrush for Transparency of Partial UIElement:
Using LinearGradientBrush allows us to define the transparency at different offsets. Lets define a LinearGradientBrush with three gradient stops with different alpha channels.

<Window x:Class="WpfApplication3.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>
<Rectangle Fill="Gray" />
<Button Height="38" Margin="20,48,11,0" Name="button1" VerticalAlignment="Top" Content="Button">
<Button.OpacityMask>
<LinearGradientBrush >
<GradientStop Color="#80021233" Offset="0"/>
<GradientStop Color="#FF021233" Offset="0.6" />
<GradientStop Color="Transparent" Offset="1"/>
</LinearGradientBrush>
</Button.OpacityMask>
</Button>
</Grid>
</Window>

When we run this, it appears as follows:



Note:
ImageBrush might also be used as OpacityMask. For ImageBrush, we must be using images which support multiple level of transparencies like PNG. Only that portion of the UIElement will be opaque which have visible alpha channel in the ImageBrush used as OpacityMask.

Wednesday, October 6, 2010

Command Line Arguments in WPF Applications

We may allow user to provide command line arguments to our WPF application. We might need to access these arguments to define the behavior of our system. But how can we access these arguments in our application? Generally we need to access these arguments when we start the application. But we might be interested to access them later in the lifetime of the application. WPF supports both of these requirements.

App_Startup Event Handler:
You can subscribe Startup Event to WPF Application. Command line arguments are available in Args property in StartupEventArgs. Here Args is a string array.

Let us specify the Startup event handler in App.xaml.

<Application x:Class="WPFCommandLineArguments.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="App_Startup" >
<Application.Resources>

</Application.Resources>
</Application>

Now we provide the definition of App_Startup handler. Here we are just accessing all the arguments provided and displaying them in a MessageBox.

public partial class App : Application
{
void App_Startup(object sender, StartupEventArgs e)
{
foreach(string arg in e.Args)
{
MessageBox.Show(arg);
}
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
}
}

In order to provide the command line paramenters, lets create a shortcut to our exe and provide some command line arguments.



Now when we run the applcation, these message box are displayed as expected:





Using Environment.GetCommandLineArguments():
When we need these arguments anywhere else in our application. We can make use of GetCommandLineArguments() in Environment. This method returns a string array so we can use this same as we used Args in previous example.


<Window x:Class="WPFCommandLineArguments.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>
<Button Content="Display Command Line Arguments" Height="47"
HorizontalAlignment="Left" Margin="151,124,0,0"
Name="button1" VerticalAlignment="Top" Width="211" Click="button1_Click" />
</Grid>
</Window>


The definition for button1_Click is as follows:

private void button1_Click(object sender, RoutedEventArgs e)
{
foreach (string arg in Environment.GetCommandLineArgs())
{
MessageBox.Show(arg);
}
}

Note: The only difference between Environment.GetCommandLineArguments() and StartupArgs.Args is that the former has the name of executable (including path) as the first element of the array.

Translating UIElement's position with respect to other UIElement

In this post, we will discuss how we can determine the position of one UIElement with respect to other element.



Let's create a view which has two button. When we click first button, the position of 2nd button (with respect to first element) is displayed in a text block.

<Window x:Class="WPFTranslate_Distance.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="472">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="71*" />
<RowDefinition Height="56*" />
<RowDefinition Height="135*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="89*" />
<ColumnDefinition Width="80*" />
<ColumnDefinition Width="109*" />
</Grid.ColumnDefinitions>
<Button Margin="14,21,14,20" Name="button1" Click="button1_Click">Button 1</Button>
<Button Grid.Column="2" Grid.Row="2" Margin="22,41,22,59" Name="button2">Button 2</Button>
<TextBlock Grid.Column="2" Margin="5,24,13,15" Name="textBlock1" />
</Grid>
</Window>

The click event of button1 is as follows:

private void button1_Click(object sender, RoutedEventArgs e)
{
Point p = this.button2.TranslatePoint(new Point(0, 0), this.button1);
this.textBlock1.Text = string.Format("X: {0}, Y: {1}", p.X, p.Y);
}

Here we are calculating the position of Top Left of Button2 with relative to Button1. When we run the application, the view appears as follows:



As we click Button 1, the position of 2nd button with respect to first button is as follows: