Dailycode.info

Short solution for short problems

WPF - MVVM: Easy wait cursor implementation

Found a very good solution on StackOverflow.

Comes down to implementing a small class, better put it somewhere in your WPF framework or Shared library.

Call it like this:

UIHelper.SetBusyState();

The cursor will return to normal when the  application stops working.

 

Code:
using System;
using System.Windows.Input;
using System.Windows.Threading;

namespace Global.Wpf
{
    public static class UIHelper
    {
        /// <summary>
        ///   A value indicating whether the UI is currently busy
        /// </summary>
        private static bool IsBusy;

        /// <summary>
        /// Sets the busystate as busy.
        /// </summary>
        public static void SetBusyState()
        {
            SetBusyState(true);
        }

        /// <summary>
        /// Sets the busystate to busy or not busy.
        /// </summary>
        /// <param name="busy">if set to <c>true</c> the application is now busy.</param>
        private static void SetBusyState(bool busy)
        {
            if (busy != IsBusy)
            {
                IsBusy = busy;
                Mouse.OverrideCursor = busy ? Cursors.Wait : null;

                if (IsBusy)
                {
                    new DispatcherTimer(TimeSpan.FromSeconds(0), DispatcherPriority.ApplicationIdle, dispatcherTimer_Tick, System.Windows.Application.Current.Dispatcher);
                }
            }
        }

        /// <summary>
        /// Handles the Tick event of the dispatcherTimer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private static void dispatcherTimer_Tick(object sender, EventArgs e)
        {
            var dispatcherTimer = sender as DispatcherTimer;
            if (dispatcherTimer != null)
            {
                SetBusyState(false);
                dispatcherTimer.Stop();
            }
        }
    }
}

Combining 2 or more columns in a listview using CellTemplate in stead of DisplayMemberBinding

Combining 2 or more columns in a listview is really simple actually. Using the DisplayMemberBinding

Property won’t get you there. This only allows 1 column to be bound. But using the CellTemplate property, you have unlimited possibilities. Just define the data template in a resource for example and bind it to the CellTemplate property. Here’s a simple example:

 

<GridViewColumn

Header="Country"

DisplayMemberBinding="{Binding Path=Country.CountryName}"

/> 

<GridViewColumn

Header="Farmer"

CellTemplate="{StaticResource FarmerTemplate}"

/>

 

 

The data template is defined in a resource file and looks like this:

 

<DataTemplate x:Key="FarmerTemplate">

    <DockPanel>

        <TextBlock HorizontalAlignment="Left">

            <TextBlock.Text>

            <Binding Path="Farmer.LastName"/>

            </TextBlock.Text>

        </TextBlock>

        <TextBlock Text=" "></TextBlock>

        <TextBlock HorizontalAlignment="Left">

            <TextBlock.Text>

            <Binding Path="Farmer.FirstName"/>

            </TextBlock.Text>

        </TextBlock>

    </DockPanel>

</DataTemplate>

 

The result:

 


Using a listview and the selecteditem with WPF and MVVM. (without touching the code behind)

The default listview by Microsoft for Wpf has no commands available. So how can we capture the selected event without using any of the code behind and still use the default controls by Microsoft.

The solution is this. We will bind the IsSelected property of the list to the IsSelected property of your data object.

<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" />

 

If the data objects (Model) that are shown in the list do not have this property, we create a wrapper class the implement the IsSelected property.

One option is to create a partial class of your DTO or model to include the IsSelected property:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Runtime.Serialization;

 

namespace Regulatory.Server.DTO

{

    public partial class Country

    {

        public Country()

        {

            this.Created = DateTime.Now;

        }

 

        private bool _isSelected;

 

        [DataMember]

        public bool IsSelected

        {

            get { return _isSelected; }

            set { _isSelected = value; }

        }

    }

}

 

 

 

Last we capture the property changed event of the object and set the selected item in your view model to the model (wrapper class) that has the IsSelected property set to true:

CountryModel countryM = new CountryModel(country);

                countryV.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(countryM_PropertyChanged);

 

public CountryModel SelectedCountry

        { get; set; }

 

void countryM_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)

        {

            string IsSelected = "IsSelected";

 

            // Make sure that the property name we're referencing is valid.

            // This is a debugging technique, and does not execute in a Release build.

            (sender as CountryModel).VerifyPropertyName(IsSelected);

 

            // When a audio record is selected or unselected, we must let the

            // world know that the TotalSelectedSales property has changed,

            // so that it will be queried again for a new value.

            if (e.PropertyName == IsSelected)

            {

                //We check if its a selected or unselected item.

                if (CountryList.Where(d => d.IsSelected == true).Contains(sender as CountryModel))

                {

                    //If its a selected item, set it as selected item in this class.

                    SelectedCountry = sender as CountryModel;

                }

            }

        }

 

Here you see no item is selected:

 

 

Next you will see that the CanSave and CanDelete react to the selecteditem changed:

 

 

 

public bool CanDeleteCountry

        {

            //Check if a country is selected.

            get { SelectedCountry != null; }

        }

 

 

 

 


ContentPresenter and tabindex

When you are using contentpresenters to show some custom controls, then you will see that there is no TabIndex property available. Instead use this:

KeyboardNavigation.TabNavigation="Local"

KeyboardNavigation.TabIndex=0

So your contentpresenter could look like this:

<ContentPresenter Content="{Binding Path=AudioTypeVM}"

                        ContentTemplate="{StaticResource AudioTypeListTemplate}"

                        HorizontalAlignment="Left" VerticalAlignment="Stretch" Grid.Column="1" Grid.Row="4" Margin="15,3,0,0"

                        KeyboardNavigation.TabNavigation="Local"

                        KeyboardNavigation.TabIndex="5">

 </ContentPresenter>

Then do not forget to arrange the tabindex of your custom control. In my case it's a dropdown list and a button:

<UserControl x:Class="MyMediaControl.Controls.CaseList"

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

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

             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

             mc:Ignorable="d"

             d:DesignHeight="24" d:DesignWidth="267">

    <DockPanel Height="24" LastChildFill="True" Width="251">

        <ComboBox DockPanel.Dock="Left"  HorizontalAlignment="Left" Name="cmbCases" VerticalAlignment="Top" MinWidth="200" ItemsSource="{Binding CaseListSource}" DisplayMemberPath="CaseID" SelectedItem="{Binding SelectedCase}" TabIndex="0"/>

        <Button DockPanel.Dock="Right" Content="New"  HorizontalAlignment="Right" Name="btnNew" VerticalAlignment="Top" MinWidth="50" Command="{Binding NewCaseCommand}" TabIndex="1"/>

    </DockPanel>

</UserControl> 


Media control database version 1

I created a small WPF application that can handle all your hard copy media like CD's, DVD's, Blue ray, records, books etc.

It wil lhelp you keep track of things when you lend out media and find them.

See more of this here.

If you like to get the source code to see the WPF/MVVM code, no problem, simply contact me and I'll provide it.

Data is stored in XML. I used linq to query the data and WPF and MVVM to show the data.


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)


'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.