J2i.Net

Nothing at all and Everything in general.

ViewModelBase and DelegateCommands in Sample Code

I'm working on some sample code for some upcoming blog post. There are two classes that are used throughout the examples and will probably be used in future samples. I wanted to mention them here for future blog post. This is applicable to both UWP and WPF projects. The first is the base class that I use for all of my ViewModel classes. 

using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Threading;


namespace Common
{
    public class ViewModelBase
    {

        public static System.Threading.SynchronizationContext SyncContext;
        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                SendOrPostCallback  a = (o) => { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); };
                if (SyncContext == null)
                    a(null);
                else
                    SyncContext.Send(a, null);
                
            }
        }

        protected void OnPropertyChanged(Expression> expression)
        {
            OnPropertyChanged(((MemberExpression)expression.Body).Member.Name);
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

The field SyncContext is needed for code that runs asynchronously on another thread (for WPF projects this is a Dispatcher instead). If the code attached to PropertyChanged interacts with the UI an exception will occur if this interaction happens on a different thread than the one on which the UI controls were created. When the OnPropertyChanged method is called the SyncContext is used to marshal control back to the UI thread.  One of the OnPropertyChanged methods takes as an argument an expression. I prefer to use this when passing the name of the field being updated to the OnPropertyChange handler because it provides the advantage of compile checking for typos in the name and will be updated if the Rename command is used on a property.  The other frequently used class(es) is the DelegateCommand

using System;
using System.Windows.Input;

namespace Common
{
    public class DelegateCommand : ICommand
    {
        public DelegateCommand(Action execute)
            : this(execute, null)
        {
        }

        public DelegateCommand(Action execute, Func canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter)
        {
            if (_canExecute != null)
                return _canExecute();

            return true;
        }

        public void Execute(object parameter)
        {
            _execute();
        }

        public void RaiseCanExecuteChanged()
        {
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, EventArgs.Empty);
        }

        public event EventHandler CanExecuteChanged;

        private Action _execute;
        private Func _canExecute;
    }

    public class DelegateCommand<T> : ICommand
    {
        public DelegateCommand(Action<T> execute)
            : this(execute, null)
        {
        }

        public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter)
        {
            if (_canExecute != null)
            {

                return _canExecute((T)parameter);
            }

            return true;
        }

        public void Execute(object parameter)
        {
            _execute((T)parameter);
        }

        public void RaiseCanExecuteChanged()
        {
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, EventArgs.Empty);
        }

        public event EventHandler CanExecuteChanged;

        private Action<T> _execute;
        private Func<T, bool> _canExecute;
    }
}

The DelegateCommand class is used to make commands that can be bound to a button. This allows us to use DataBinding to associate code with a button through data binding. 

Setting the Display for a WPF Application

I'm working on a presenttion project for which I needed to set the display on which a set of WPF applications are running. There's a total of 6 WPF applications that will be running on the same machine at once. Each application is going to be running on a different display. I thought I would share the solution that I used to control which display that each instance of this application went to. 

The first thing I needed to do was to retrieve the information on how how many displays that the computer has and the coordinates of each display. The computer creates a single (logical) display and maps each display device to cover a range of coordinates on this logical display. So I needed to retrieve a list of the displays and the coordinates to which it is mapped. The class available for doing this is in in the System.Windows.Forms library. Since I wasn't making a Windows Forms application I didn't want to add a using directive that would include the entire library; if I did there would be some class names that exists both in this name space and a WPF name space that I was using that could cause some resolution issues. So I only included the single class from the namespace. 

using Screen=System.Windows.Forms.Screen

The Screen class contains a member named <code>AllScreens</code> that contains a collection of <code>Screen</code> objects that give the information on each screen. If you wanted to make a simple WPF program that displayed all of the screens and their positions it only takes a few lines of code. The following is the code for the code-behind and the XAML for such a program. 

public partial class MainWindow : Window
{

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = Screen.AllScreens;
    }
}
<Window x:Class="ScreenTest.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>
        <ListBox ItemsSource="{Binding}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Vertical">
                        
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="100" />
                                <ColumnDefinition Width="100" />
                                <ColumnDefinition Width="100" />
                                <ColumnDefinition Width="100" />
                                <ColumnDefinition Width="100" />
                            </Grid.ColumnDefinitions>
                            <TextBlock Text="{Binding DeviceName}" />
                            <TextBlock Grid.Column="1" Text="{Binding WorkingArea.X}" />
                            <TextBlock Grid.Column="2" Text="{Binding WorkingArea.Y}" />
                            <TextBlock Grid.Column="3" Text="{Binding WorkingArea.Width}" />
                            <TextBlock Grid.Column="4" Text="{Binding WorkingArea.Height}" />
                        </Grid>
                    </StackPanel>
                    
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>
 

Screenshot of the interface running

 

For the programs that I'll be running I've set device name for the program's intended display in a configuration file. The typical display name for the first display is \\.\DISPLAY1. I've made a method that will take the indended display name, try to find it, and set the position and size of the window accordingly.

void SetTargetDisplay()
{
    var targetDeviceName = Settings.Default.DisplayDevice;
    if(!String.IsNullOrEmpty(targetDeviceName))
    {
        // see if the device name specified exists here. It's possible
        // this was configured  to  run on a different machine and the 
        // configured device might not exists
        var screen = (from s in Screen.AllScreens 
             where s.DeviceName.ToLower().Equals(targetDeviceName.ToLower()) 
             select s).FirstOrDefault();
        if (screen != null)
        {
            Left = screen.WorkingArea.Left;
            Top = screen.WorkingArea.Top;
            Width = screen.WorkingArea.Width;
            Height = screen.WorkingArea.Height;
        }
    }
}

If the display doesn't exists (which could happen because of a typographical error or the program having been configured for another machine) then the method will just ignore the request.