Dailycode.info

Short solution for short problems

Fire and event from a sub thread to the main thread in WPF

This small example shows you how to simply fire an event from a sub thread, capture it In the main thread and handle it in WPF.

The demo application (Attached to this post) simply starts a sub thread. In this thread we generate some random numbers and sent them back to the main thread. The main thread will show these numbers in a list.

First let’s investigate the main thread.

I’m using 2 private objects in my main thread:

private Dispatcher _uiDispatcher;

Thread _workerThread;

 

The _uiDispatcher will be used to store the main thread dispatcher in. The workerThread is the thread that will be used to handle the subroutine.

When I initiate the mainform, I will store the dispatcher of the main thread in the _uiDispatcher.

// Call from the main thread  

public void UseThisThreadForEvents()

{     

_uiDispatcher = Dispatcher.CurrentDispatcher;

} 

       

public MainWindow()

{

InitializeComponent();

       UseThisThreadForEvents();

}

 

So when the program starts, we have the dispatcher of the main thread that will be responsible to update the UI stored in a variable.

On the main form there is a button, when clicked it will start the sub thread.

private void button1_Click(object sender, RoutedEventArgs e)

{

    ThreadStart start;

    Worker worker = new Worker();

    worker.OnValueSent += new Worker.SentValueHandler(worker_OnValueSent);

    start = delegate()

    {

        worker.SentValuesEvery2Seconds();

    };

 

    // Create the thread and kick it started!

    _workerThread = new Thread(start);

    _workerThread.Priority = ThreadPriority.Lowest;

    _workerThread.Start();

           

}

 

In my worker class I declared an event that will be fired every 2 seconds. This event will be captured in the main thread and handled by the worker_OnValueSent method.

So here is where we will have to check in which thread we are and if needed invoke the dispatcher of the main thread so we can update the UI without invoking every control separately.

public delegate void OnValueSentDelegate(List<int> list);

 

void worker_OnValueSent(List<int> list)

{

    if (Dispatcher.CurrentDispatcher != _uiDispatcher)

    {

        _uiDispatcher.BeginInvoke(new OnValueSentDelegate(worker_OnValueSent), list);

 

    }

    else

    {

        listView1.Items.Add(list[0]);

        listView1.Items.Add(list[1]);

        listView1.Items.Add(list[2]);

    }

}

 

Simple logic written here. When the event is fired, we check if the dispatcher equals the _uiDispatcher (Dispatcher of the main thread). If not, we invoke the main thread dispatcher and use a delegate method to call the OnValueSent method.

So that’s how we do it.

For the people who need it, here the simple code of the worker class:

public class Worker

{

    public delegate void SentValueHandler(List<int> list);

 

    public event SentValueHandler OnValueSent;

 

    public void SentValuesEvery2Seconds()

    {

        int test = 0;

        while (test < 5)

        {

            Thread.Sleep(2000);

            if (OnValueSent != null)

            {

                Random rd = new Random();

 

                OnValueSent(new List<int>() { rd.Next(0, 1000), rd.Next(0, 1000), rd.Next(0, 1000) });

            }

            test++;

        }

    }

}

 

And here is a screenshot to give you an idea of what it does and how it looks.

 

 

WpfApplication1.zip (50.11 kb)


Tank calculator

Ever had the problem that you were moving and some oil was left in the oil tank. But then there were no signs or measurements available to help you calculate the volume of what is left in the tank.

Well, this site had a good solution which I already used in my previous house. But it didn't work anymore. so I decided to quickly create one of my own: TANK CALCULATOR.

It took me 1 hour to fix this thanks to the rapid development in ASP.Net

Here's a screenshot:

 

 

 And its also in dutch available:

Om de inhoud van je stookolie (mazout) tank te berekenen, klik hier.

 Screenshot:

 

 

 

 

 


'Resources' property has already been set on

I was trying to build a nice WPF application, where I use general styles for some buttons. Then at a moment in time I got this nise error: ''Resources' property has already been set on. Complete it looked like this: ''Resources' property has already been set on 'AllAudioView'.' Line number '14' and line position '11'.
So I looked around and finally found what the problem was.
In my code I used a referenced resource file combined with some static resources. It looked like this:
<UserControl.Resources>
    <ResourceDictionary x:Key="Resc">
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/Style.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
    <CollectionViewSource x:Key="AudioRecords" Source="{Binding Path=AllAudioRecords}">
        <CollectionViewSource.SortDescriptions>
            <scm:SortDescription PropertyName="Number" Direction="Descending" />
        </CollectionViewSource.SortDescriptions>
    </CollectionViewSource>
</UserControl.Resources>
 
As  you see there is a reference to a resource dictionary and behind this a collection view source, which is only used for this control, so it makes sense to put it there. But Microsoft doesn’t think so.
So the only way to solve this is when you reference a resource dictionary, put all your other resources in a separate dictionary file.
I created a second file only for the usercontrol and named it: AllAudioViewResources.xaml. I put all the resources belonging to the usercontrol in this file.
Then I replaced the code in the usercontrol to look like this:
<UserControl.Resources>
    <ResourceDictionary x:Key="Resc">
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/Style.xaml" />
            <ResourceDictionary Source="AllAudioViewResources.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</UserControl.Resources>
 
And yes, now it works!
 
The resource dictionary file looks like this:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

                    xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"

                    >

   

    <CollectionViewSource x:Key="AudioRecords" Source="{Binding Path=AllAudioRecords}">

        <CollectionViewSource.SortDescriptions>

            <scm:SortDescription PropertyName="Number" Direction="Descending" />

        </CollectionViewSource.SortDescriptions>

    </CollectionViewSource>

</ResourceDictionary>

 

Sort a list view with WPF and MVVM

Sorting a grid with WPF and MVVM is not that easy as it was in Forms. Still it’s very easy to do. Here a straight forward example where I will sort my data on Number or Title.

 

First you will have to implement the Sorting Commands and functions in your viewmodel:

#region Sorting

 

public ICommand SortCommand

{

    get

    {

        if (_sortCommand == null)

        {

            _sortCommand = new RelayCommand(

                param => this.Sort(),

                param => this.CanSort

                );

        }

        return _sortCommand;

    }

}

 

As you can see I used a relay command to handle the command binding. The sort method will be executed when the sort button is clicked. The can sort will help the GUI to see if the command can be executed. I simply check if one of the radio boxes is checked.

 

private void Sort()

{

    List<AudioEnt> audioEntList = null;

    if (ByNumber)

    {

        audioEntList = GenericSingleton<RepositoryHold>.GetInstance().AudioRep.LoadAudioRecords();

        audioEntList.Sort((s1, s2) => s1.Number.CompareTo(s2.Number));

    }

    else if (ByTitle)

    {

        audioEntList = GenericSingleton<RepositoryHold>.GetInstance().AudioRep.LoadAudioRecords();

        audioEntList.Sort((s1, s2) => s1.Title.CompareTo(s2.Title));

    }

    CreateAllAudioRecords(audioEntList);

}

 

bool CanSort

{

    get

    {

        return (ByTitle || ByNumber ? true : false);

    }

}

 

#endregion

 

I also expose these properties to handle the choice of sorting:

public bool ByNumber { get; set; }

 

public bool ByTitle { get; set; }

 

Now the only thing we need to do is bind this command to the GUI.

<RadioButton Content="By Number" IsChecked="{Binding Path=ByNumber}"/>

<RadioButton Content="By Title" IsChecked="{Binding Path=ByTitle}" />

<Button Content="Find" Command="{Binding Path=FindCommand}" />

 

 

 

 


Horizontal topnavigation with MVVM and WPF

 

When I was trying to set up a nice framework project that could handle my initial small media management project, I bumped into several designed issues. It was not so easy to find solutions, since I had a hard time looking for the correct Google words to get the info I needed.

The small project is to become a media database where you can insert your media, control it and even manage lend outs. I wanted it to have a top navigation bar and a side navigation. This article is about the top navigation. I’m implementing the MVVM design pattern, that’s what makes it a little more difficult and that’s exactly why I’m posting now.

I made a usercontrol called TopBarButton, which should be used to do the top navigation. Then I made a topcommandview model which exposes the properties the buttons will need to bind to.

This class looks like this:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows.Input;

 

namespace MyMediaControl.ViewModel

{

    /// <summary>

    /// Represents an actionable item displayed by a View.

    /// </summary>

    public class TopCommandViewModel : ViewModelBase

    {

        private string _imageSource;

 

        public string ImageSource

        {

            get { return _imageSource; }

            set { _imageSource = value; }

        }

 

 

        public TopCommandViewModel(string displayName, ICommand command, string imageSource)

        {

            if (command == null)

                throw new ArgumentNullException("command");

 

            base.DisplayName = displayName;

            this.Command = command;

            this._imageSource = imageSource;

        }

 

        public ICommand Command { get; private set; }

    }

}

 

The image source property is to show an image on my button.

Last I need to create a collection in my main window view and bind them to the top navigation bar.

In my main window view model I create a read only collection of TopCommendViewModel.

/// <summary> 

/// Returns a read-only list of commands  

/// that the UI can display and execute.

/// </summary>

public ReadOnlyCollection<TopCommandViewModel> TopCommands

{

    get

    {

        if (_topCommands == null)

        {

            List<TopCommandViewModel> cmds = this.CreateTopCommands();

            _topCommands = new ReadOnlyCollection<TopCommandViewModel>(cmds);

        }

        return _topCommands;

    }

}

 

Here I create the TopCommandViewModel list and you can see I pass 3 parameters to it. First the title of the button coming from a resource file called Strings, second the function to execute and third the image path for the button.

 

List<TopCommandViewModel> CreateTopCommands()

{

    return new List<TopCommandViewModel>

    {

        new TopCommandViewModel(

            Strings.MainWindowViewModel_Command_OpenAudio,

            new RelayCommand(param => this.OpenAudioCommands()),"/Images/audio_icon_120.gif"),

 

        new TopCommandViewModel(

            Strings.MainWindowViewModel_Command_OpenVideo,

            new RelayCommand(param => this.OpenVideoCommands()),"/Images/movie_icon_120.gif"),

        new TopCommandViewModel(

            Strings.MainWindowViewModel_Command_OpenBook,

            new RelayCommand(param => this.OpenBookCommands()),"/Images/books_icon_120.gif")

    };

}

 

When this is done I still need to do the binding in my View. For this we need to work in 2 places. First place is a resource file which is linked to my main window. XAML and next I will add a content presenter to the XAML of my main window.

Here is the code for the data template which can be found in the resource file, this is the code as I where I started with, later I will add something extra to show you the result:

<DataTemplate x:Key="TopCommandsTemplate" >

        <ItemsControl IsTabStop="False" ItemsSource="{Binding}" Margin="0" >

            <ItemsControl.ItemTemplate>

                <DataTemplate>

                    <my:TopNavButton Text="{Binding Path=DisplayName}" Image="{Binding Path=ImageSource}"

                    ImageWidth="70" ImageHeight="70" Margin="5" ></my:TopNavButton>

                </DataTemplate>

            </ItemsControl.ItemTemplate>

        </ItemsControl>

    </DataTemplate>

 

Last we define the place where we want the navigation buttons to show up:

<Canvas Height="100" Name="spTopNav"  Width="Auto" MinWidth="400" Style="{StaticResource MainNAVStyle}" Margin="0">

    <ContentPresenter Canvas.Left="0" Canvas.Top="0"

        Content="{Binding Path=TopCommands}"

    ContentTemplate="{StaticResource TopCommandsTemplate}"

    HorizontalAlignment="Left" VerticalAlignment="Stretch"

    />

</Canvas>

 

Now here how this all looks like in design time view:

TopNavBarDesign.jpg

So at this time when I run the project, it looks like this:

TopNavBarNOK.jpg

If you look closely, you will discover that just below the first top navigation button, the second one is rendered but not visible or usable for the user. I tried to find a solution and had to do a lot of google searches until I ended up with the solution, using the items panel template.

So I added this to the data template of my topcommandsbutton template:

<DataTemplate x:Key="TopCommandsTemplate" >

    <ItemsControl IsTabStop="False" ItemsSource="{Binding}" Margin="0" >

        <ItemsControl.ItemTemplate>

            <DataTemplate>

                <my:TopNavButton Text="{Binding Path=DisplayName}" Image="{Binding Path=ImageSource}"

                ImageWidth="70" ImageHeight="70" Margin="5" ></my:TopNavButton>

            </DataTemplate>

        </ItemsControl.ItemTemplate>

        <ItemsControl.ItemsPanel>

            <ItemsPanelTemplate>

                <StackPanel Orientation="Horizontal"/>

            </ItemsPanelTemplate>

        </ItemsControl.ItemsPanel>

    </ItemsControl>

</DataTemplate>

 

Now it will show the items in a stackpanel which is oriented horizontally. Now it looks like this:

TopNavBarOK.jpg


Multi-Threading or Cross threading and accessing controls from seperate threads in WPF

I started to build my first real WPF application. It’s a former windows application that I’m rebuilding and improving using WPF and WCF. Former it was Windows forms and Web services.

One of the first problems I encountered was when I was experimenting with multithreading. I start my main thread and from there I will start a background thread. This background thread will start sub thread of its own. Still not a big issue, until I wanted to report the status from my background thread to my main thread. At this point it’s slightly different in WPF then it was in windows forms. And for some reason Microsoft decided to hide some methods in Visual Studio, so this makes it even harder.

The base article from which I constructed my application can be found is Avalon Threading by Tyler Barton and Nick Kramer.

So it all starts when you start a process in a separate thread, I decided to start a background worker thread process like this:

 

/// <summary>

/// Loads the devices and starts Polling

/// </summary>

public void LoadDevices(List<ThinClient> thinClients)

{

    BackgroundWorker bw = new BackgroundWorker();         

    // this allows our worker to report progress during work        

    bw.WorkerReportsProgress = true;         

    // what to do in the background thread        

    bw.DoWork += new DoWorkEventHandler(delegate(object o, DoWorkEventArgs args)

        {

            LoadDevicesInThread(thinClients);       

        });         

   

    bw.RunWorkerAsync();

           

}

 

The actual work is done in the LoadDevicesInThread function. This function will loop over a list of TCP devices and will try to poll them, using a poll time out of 2 seconds. We do not want this to block the main thread, so that’s why it’s handled in the background. Still after each device I want the background thread to report the status to my GUI in the main thread.

That’s why I created these 3 methods. The background worker process is started in a sub class, where I created an event that will be raised whenever I want to log something back to the GUI.

 

public delegate void LogHandler(string type, string message);

 

public event LogHandler Log;

 

protected void OnLog(string type, string message)

{

    if (Log != null)

    {

        Log(type, message);

    }

}

 

So every time I call the OnLog method, the Log event  will be raised and captured by the GUI. Here were I do the binding to the event in the main window:

 

_ourDevices.Log += new Devices.LogHandler(_ourDevices_Log);

 

So the LogHandler will handle the loggings coming from the background thread and pass them to the GUI. This is where the tricky bit comes in. In Windows forms we had to call the InvokeRequired method, but when you look for this or something like this, you will find nothing in intellisense. Why you may ask. Because for WPF the InvokeRe quired doesn’t exists but has a “replacement” called CheckAccess. They decided to hide this method from the Visual Studio intellisense.  So when you want to invoke a control from a separate thread, this is the way it’s done:

 

public delegate void LogMessageDelegate(string type, string message);

 

void _ourDevices_Log(string type, string message)

{

    if (lstLogItems.CheckAccess())

    {

        lstLogItems.Items.Add(new InfoMessage(type, message));

    }

    else

    {

        lstLogItems.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Send, new LogMessageDelegate(this._ourDevices_Log),type, message);

    }

}

 

So here you can see that when the ListView called lstLogItems is accessed from a separate thread, he will end up in the else and do a begininvoke. He will use the delegate function to accomplish this and pass the parameters to it.

So I hope it become a little clearer this way. I posted an extension method on my blog for windows forms cross threading: http://www.dailycode.info/Blog/post/2011/10/12/Setting-the-property-of-a-control-from-a-seperate-thread-using-an-extension-method.aspx. You can try to rewrite this for WPF, if the need comes I will do it and post this on the Blog.

 


Start and stop animations in WPF from code behind

It sounds simple, and it is.

I followed the simple Microsoft demonstration which you can find here.

I had a start and stop button and defined a trigger on the button to start the animation when it was clicked. Still was working fine. Then I decided to use the same button to start and stop the animation and this is where I got stuck for a short while.

The trigger on the XAML defined that each time the click was called, the event should be triggered. But when I stopped the animation in the onclick, the animation was still running. So this means the trigger is executed after the onclick event is handled. To solve this I removed the trigger and placed the storyboard with the animations in the resources:

<Window.Resources>

        <Storyboard x:Key="ButtonStoryBoard">

            <DoubleAnimation Name="btnStartdoubleAnimation"

                                        Storyboard.TargetName="btnStart"

                                        Storyboard.TargetProperty="Opacity"

                                        From="1.0" To="0.0" Duration="0:0:5"

                                        AutoReverse="True" RepeatBehavior="Forever" />

            <ColorAnimation Storyboard.TargetName="MyColorBrush" 

                                        Storyboard.TargetProperty="Color"

                                        From="Green"

                                        To="Yellow"              

                                        Duration="0:0:10"

                                        AutoReverse="True" RepeatBehavior="Forever"/>

            <ColorAnimation Storyboard.TargetName="MyBorderColorBrush" 

                                        Storyboard.TargetProperty="Color"

                                        From="DeepSkyBlue"

                                        To="AntiqueWhite"              

                                        Duration="0:0:15"

                                        AutoReverse="True" RepeatBehavior="Forever"/>

 

        </Storyboard>

    </Window.Resources>

Then I removed the trigger in XAML and simply started and stopped the storyboard animations in code.

It looks like this:

Storyboard storyboard;

 

        private void btnStart_Click(object sender, RoutedEventArgs e)

        {

            storyboard = this.TryFindResource("ButtonStoryBoard") as Storyboard;

 

            if (!_started)

            {

               

                if (storyboard != null)

                {

                    storyboard.Begin(btnStart, true);

 

                }

 

                _started = true;

                btnStart.Content="Stop";

            }

            else

            {

                _started = false;

 

                if (storyboard != null)

                {

                    storyboard.Stop(btnStart);

                }

 

 

                btnStart.Content = "Start";

            }

        }

So the animations are started and stopped from the code behind.

Found most of the info here.