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();
            }
        }
    }
}

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; }

        }

 

 

 

 


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.


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