Hi there,

 

Although this post is very informative, it didn’t quite fulfill my needs.

I needed to start an asynchronous method, lazy loading some data, when a property bound to a WPF control was updated.

This is what I came up with:

 

public void UpdateInformation(string propertyValue)
{
	System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke(new Action(async () =>
	{
		_cancellationTokens.ForEach(c => c.Cancel());
		using (CancellationTokenSource cancellationToken = new CancellationTokenSource())
		{
			try
			{
				_cancellationTokens.Add(cancellationToken);
				var dataResponse = await service.LoadData(propertyValue, cancellationToken);
				...
			}
			finally
			{
				_cancellationTokens.Remove(cancellationToken);
			}
		}
	}));
}

As always, feel free to ask or comment.


Cancel an async task

Published 12/20/2018 by Christian in Code
Tags: , , ,

So, I wanted to do asynchronously database search that could take a long time. But if the user refine her search criteria, the async should restart, and whatever already found records be disregarded.

Using the following Microsoft article as a template, this is what I came up with as a proof of concept.

 https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/cancel-an-async-task-or-a-list-of-tasks

 

This WPF program has 3 buttons. 

Start - will cancel all already started jobs, waits for 5 sec. and then download some content from a msdn site.

Cancel All - will simply cancel all started jobs.

Clear - will cancel all started jobs, and clear any messages.

 

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows;

// Add a using directive and a reference for System.Net.Http.
using System.Net.Http;

// Add the following using directive for System.Threading.
using System.Threading;

namespace CancelATask
{
    public partial class MainWindow : Window
    {
        // ***Declare a System.Threading.CancellationTokenSource.
        List<CancellationTokenSource> cts = new List<CancellationTokenSource>();

        public MainWindow()
        {
            InitializeComponent();
        }


        private async void startButton_Click(object sender, RoutedEventArgs e)
        {
            cts?.ForEach(c => c.Cancel());
            // ***Instantiate the CancellationTokenSource.
            using (CancellationTokenSource c = new CancellationTokenSource())
            {
                cts.Add(c);

                try
                {
                    // ***Send a token to carry the message if cancellation is requested.
                    int contentLength = await AccessTheWebAsync(c.Token);
                    resultsTextBox.Text += $"\r\nLength of the downloaded string: {contentLength}.\r\n";
                }
                // *** If cancellation is requested, an OperationCanceledException results.
                catch (OperationCanceledException)
                {
                    resultsTextBox.Text += $"\r\nDownload canceled. Current no of jobs: {cts.Count}\r\n";
                }
                catch (Exception)
                {
                    resultsTextBox.Text += "\r\nDownload failed.\r\n";
                }
                cts.Remove(c);
            }
        }


        // ***Add an event handler for the Cancel button.
        private void cancelButton_Click(object sender, RoutedEventArgs e)
        {
            cts?.ForEach(c => c.Cancel());
        }

        private void clearButton_Click(object sender, RoutedEventArgs e)
        {
            cts?.ForEach(c => c.Cancel());
            resultsTextBox.Clear();
        }

        // ***Provide a parameter for the CancellationToken.
        async Task<int> AccessTheWebAsync(CancellationToken c)
        {
            HttpClient client = new HttpClient();

            resultsTextBox.Text += "Waiting for download to commence.\r\n";

            // You might need to slow things down to have a chance to cancel.
            await Task.Delay(5000, c);

            resultsTextBox.Text += "Commencing to download.\r\n";
            // GetAsync returns a Task<HttpResponseMessage>. 
            // ***The c argument carries the message if the Cancel button is chosen.
            HttpResponseMessage response = await client.GetAsync("http://msdn.microsoft.com/en-us/library/dd470362.aspx", c);

            // Retrieve the website contents from the HttpResponseMessage.
            byte[] urlContents = await response.Content.ReadAsByteArrayAsync();

            // The result of the method is the length of the downloaded web site.
            return urlContents.Length;
        }
    }

    // Output for a successful download:

    // Ready to download.

    // Length of the downloaded string: 158125.


    // Or, if you cancel:

    // Ready to download.

    // Download canceled.
}

 

As always feel free to ask or comment.


This one gave me some headache, but as always, looking back the solution is pretty obvious.

Using WPF, in a MVVM design pattern, I have a ListView bound to a ObservableCollection. Straight forward senario.

XAML:

<ListView Name="lvCustomerSearchResults" ItemsSource="{Binding CustomerSearchResults}" SelectedItem="{Binding SelectedCustomer}" ...

 

ViewModel:

 

public async Task<AOTaskResult<List<CustomerSearchResultEntity>>> SearchCustomers(string cvrNo, Statuses? status = null, DateTime? fromDate = null, DateTime? toDate = null)
{
    return await Task.Run(() =>
    {
        AOTaskResult<List<CustomerSearchResultEntity>> result = new AOTaskResult<List<CustomerSearchResultEntity>>();
        try
        {
            result.Response = _repository.LstCustomers(cvrNo, status, fromDate, toDate, out List<NezCusCustomerDTO> dtos);
            if (result.Response.Success)
            {
                result.Result = dtos.Select(d => LocalMapping.ToCustomerSearchResultEntity(d)).ToList();
            }
        }
        catch (Exception ex) { CheckTaskException(ex); }
        return result;
    });
}

 

Trouble is when I click an item in the ListWiew I get a “Must create DependencySource on same Thread as the DependencyObject”-exception. It turns out that the reason I get this exception is that I created the items in the ObservableCollection on another thread than the main-thread. So, obviously, the solution is to create the the ObservableCollection items on the main-thread:

public async Task<AOTaskResult<List<CustomerSearchResultEntity>>> SearchCustomers(string cvrNo, Statuses? status = null, DateTime? fromDate = null, DateTime? toDate = null)
{
    AOTaskResult<List<CustomerSearchResultEntity>> result = new AOTaskResult<List<CustomerSearchResultEntity>>();
    List<NezCusCustomerDTO> dtos = null;

    await Task.Run(() =>
    {
        try
        {
            Busy = true;
            result.Response = _repository.LstCustomers(cvrNo, status, fromDate, toDate, out dtos);

            Busy = false;
        }
        catch (Exception ex) { CheckTaskException(ex); }
    });
    if (dtos != null && result.Response.Success)
    {
        result.Result = dtos.Select(d => LocalMapping.ToCustomerSearchResultEntity(d)).ToList();
    }
    return result;
}

 

 IMO, the code is not as clean ... but it works :-)

 

As always feel to ask or comment.


A Simple WPF DataGrid using MVVM

Published 9/15/2017 by Christian in Code
Tags: , , ,

Her follows a simple WPF DataGrid.

In the Visual Studio Design view it look like this:

The DefaultButtonStyle and DefaultDataGrid style can be found elsewhere in this blog.

Ill include the XAML for the entire Window.

<Window x:Class="Ellab.Main.Sessions.Chamber.WPFUnitSelection.UnitSelectionView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Ellab.Main.Sessions.Chamber.WPFUnitSelection"
        mc:Ignorable="d"
        Title="{Binding Title}" x:Name="MainWindow" Height="300" Width="750">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/Ellab.WPF;component/View/Styles/ButtonStyle.xaml"/>
                <ResourceDictionary Source="pack://application:,,,/Ellab.WPF;component/View/Styles/DataGridStyle.xaml"/>
                <ResourceDictionary Source="MvvmResources.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid Background="{Binding Background, FallbackValue=Red}">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="40"/>
        </Grid.RowDefinitions>
        <DataGrid Grid.Row="0" ItemsSource="{Binding Rows, FallbackValue=AllTheSmartRows}" Style="{StaticResource DefaultDataGrid}" HeadersVisibility="Column" AutoGenerateColumns="False" VerticalAlignment="Top" SelectionMode="Single" SelectionUnit="FullRow" SelectedItem="{Binding SelectedItem}">
            <DataGrid.RowStyle>
                <Style TargetType="{x:Type DataGridRow}">
                    <EventSetter Event="MouseLeftButtonDown" Handler="DataGrid_MouseLeftButtonDown"/>
                    <Style.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="Background" Value="#FF009CDD" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </DataGrid.RowStyle>
            <DataGrid.Columns>
                <DataGridTextColumn Header="{Binding UnitIDHeader, FallbackValue=ID}" Binding="{Binding UnitID, FallbackValue=ID}" MinWidth="75" Width="Auto" IsReadOnly="True"/>
                <DataGridTextColumn Header="{Binding TypeHeader, FallbackValue=Type}" Binding="{Binding Type, FallbackValue=Type}" MinWidth="75" Width="Auto" IsReadOnly="True"/>
                <DataGridTextColumn Header="{Binding UnitTypeHeader, FallbackValue=Unit Type}" Binding="{Binding UnitType, FallbackValue=Unit}" MinWidth="75" Width="Auto" IsReadOnly="True"/>
                <DataGridTextColumn Header="{Binding NameHeader, FallbackValue=Name}" Binding="{Binding Name, FallbackValue=Name}" MinWidth="125" Width="Auto" IsReadOnly="True"/>
                <DataGridTextColumn Header="{Binding DescriptionHeader, FallbackValue=Description}" Binding="{Binding Description, FallbackValue=Description}" MinWidth="25" Width="*" IsReadOnly="True"/>
            </DataGrid.Columns>
        </DataGrid>
        <Grid Grid.Row="1" Background="White" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Height="40">
            <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right">
                <Button Margin="5" Command="{Binding HelpClickedCommand}" Width="30" Style="{StaticResource DefaultButtonStyle}" Background="#FF123F74">
                    <Image Source="../../Resources/Help.png"/>
                </Button>
                <Button Margin="5" Content="{Binding ViewButtonContent, FallbackValue=#View}" Command="{Binding ViewClickedCommand}" Style="{StaticResource DefaultButtonStyle}" Visibility="{Binding ViewVisible}" MinWidth="75"/>
                <Button Margin="5" Content="{Binding AddNewButtonContent, FallbackValue=#Add New}" Command="{Binding AddNewClickedCommand}" Style="{StaticResource DefaultButtonStyle}" Visibility="{Binding AddNewVisible}" MinWidth="75"/>
                <Button Margin="5" Content="{Binding EditButtonContent, FallbackValue=#Edit}" Command="{Binding EditClickedCommand}" Style="{StaticResource DefaultButtonStyle}" Visibility="{Binding EditVisible}" MinWidth="75"/>
                <Button Margin="5" Content="{Binding RemoveButtonContent, FallbackValue=#Remove}" Command="{Binding RemoveClickedCommand}" Style="{StaticResource DefaultButtonStyle}" Visibility="{Binding RemoveVisible}" MinWidth="75"/>
                <Button Margin="5" Content="{Binding ImportButtonContent, FallbackValue=#Import}" Command="{Binding ImportClickedCommand}" Style="{StaticResource DefaultButtonStyle}" Visibility="{Binding ImportVisible}" MinWidth="75"/>
                <Button Margin="5" Content="{Binding ExportButtonContent, FallbackValue=#Export}" Command="{Binding ExportClickedCommand}" Style="{StaticResource DefaultButtonStyle}" Visibility="{Binding ExportVisible}" MinWidth="75"/>
                <Button Margin="5" Content="{Binding OKButtonContent, FallbackValue=#OK}" Command="{Binding OKClickedCommand}" Style="{StaticResource DefaultButtonStyle}" Visibility="{Binding OKVisible}" IsDefault="True" MinWidth="75"/>
                <Button Margin="5" Content="{Binding CloseButtonContent, FallbackValue=#Close}" Command="{Binding CloseClickedCommand}" Style="{StaticResource DefaultButtonStyle}" Visibility="{Binding CloseVisible}" IsCancel="True" MinWidth="75"/>
            </StackPanel>
        </Grid>
    </Grid>
</Window>

Allthough using the MVVM design patten, I've opted to handel  event in the codebehind, as in my opinion this is View releated. Here is the event handler

namespace Ellab.Main.Sessions.Chamber.WPFUnitSelection
{
    /// <summary>
    /// Interaction logic for UnitSelectionView.xaml
    /// </summary>
    public partial class UnitSelectionView : Window
    {
        private UnitSelectionView()
        {
            InitializeComponent();
        }

        public UnitSelectionView(UnitSelectionViewModel mainContext)
            : this()
        {
            DataContext = mainContext;
        }
        private void DataGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            DataGridRow clickedRos = sender as DataGridRow;
            clickedRos.IsSelected = true;
        }
    }
}

As always, feel free to comment, or ask.


The System.Linq.Distinct extension returns a list (or more to the point, an IEnumerable<>) of ... surprise ... distinct items in a list.

This is all well and good for simple data types (e.g. integers). But what if you have more complex data types, and you want to get a distinct list based on a specific property.

Well - Distinct extension enumerates the Object.Equals method on all items in the list, and returns a new list list based on the result. An object is only equal to another object if they have the same hash code. So running List<T>.Distinct() would only exclude items from List<T> where T are the same object, and not items with the same information.

Say you have a Student type:

public class Student
{
    public long ID { get; set; }
    public string Name { get; set; }

    public Student(long id, string name)
    {
        ID = id;
        Name = name;
    }
}

And a Class:

public class Class
{
    private List<Student> _students { get; } = new List<Student>() { new Student(1, "John Doe"), new Student(1, "John Doe"), new Student(2, "Jane Doe") };
}

Running 

IEnumerable<Student> distinctStudent = _students.Distinct();

would give you 3 items, as both "John Doe" have unique HashCode - they are not the same object.

There are two ways of getting about this.

Either override Object.Equals(object obj) and Object.GetHashCode() on the Student type:

public class Student
{
    public long ID { get; set; }
    public string Name { get; set; }

    public Student(long id, string name)
    {
        ID = id;
        Name = name;
    }

    public override bool Equals(object obj)
    {
        var student = obj as Student;
        return this.ID.Equals(student?.ID);
    }
    public override int GetHashCode()
    {
        return ID.GetHashCode();
    }
    public override string ToString()
    {
        return Name;
    }
}

This executes fairly fast, but you would only be able to get unique items based on their ID.

If you want a more customisable way, you could write an extension:

public static class Extensions
{
    public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    {
        HashSet<TKey> seenKeys = new HashSet<TKey>();
        foreach (TSource item in source)
        {
            if (seenKeys.Add(keySelector(item)))
            {
                yield return item;
            }
        }
    }
}

and user it like this:

public class Class
{
    private List<Student> _students { get; } = new List<Student>() { new Student(1, "John Doe"), new Student(1, "John Doe"), new Student(2, "Jane Doe") };

    public IEnumerable<Student> GetDistinct()
    {
        return _students.Distinct();
    }

    public IEnumerable<Student> GetDistinctBy()
    {
        return _students.DistinctBy(s => s.ID);
    }

    public IEnumerable<Student> GetDistinctByMore()
    {
        return _students.DistinctBy(s => new { s.ID, s.Name });
    }
}

As always, feel free to comment, or ask.


Getting an ancestor property value can be done by binding to its element name. Here's an example

<Grid Width="{Binding Width}" Margin="{Binding Margin}">
	<Border BorderBrush=" #FF17447E" BorderThickness="5">
		<StackPanel Name="MainStackPanel" Orientation="Vertical">
			<ScrollViewer Height="{Binding ElementName=MainStackPanel, Path=ActualHeight}">
				<ItemsControl ItemsSource="{Binding Rows, FallbackValue=Rows}" Background="Transparent">
					<ItemsControl.ItemsPanel>
						<ItemsPanelTemplate>
							<StackPanel Orientation="Vertical"/>
						</ItemsPanelTemplate>
					</ItemsControl.ItemsPanel>
					<ItemsControl.ItemTemplate>
						<DataTemplate>
							<ContentPresenter Content="{Binding}" Height="30"/>
						</DataTemplate>
					</ItemsControl.ItemTemplate>
				</ItemsControl>
			</ScrollViewer>
		</StackPanel>
	</Border>
</Grid>

As always, feel free to comment, or ask


C# Value Types Default Values

Published 2/25/2016 by Christian in Code
Tags:

According to: https://msdn.microsoft.com/da-dk/library/83fhsxwc.aspx

the default values of C# Value Types, are as followes:

Value type

Default value

bool

false

byte

0

char

'\0'

decimal

0.0M

double

0.0D

enum

The value produced by the expression (E)0, where E is the enum identifier.

float

0.0F

int

0

long

0L

sbyte

0

short

0

struct

The value produced by setting all value-type fields to their default values and all reference-type fields to null.

uint

0

ulong

0

ushort

0

As always, feel free to comment, or ask.


OK - So I’ve got a WPF DataGrid, with a DataGridTemplateColumn. The DataTemplate is a plane TextBox. When I tab through the cells, I want to select the text in the TextBox. And when I click the text in the TextBox, I also want to select it. That proved more difficult than I expected.

But hacking and slashing from this blog

http://andora.us/blog/2011/06/09/wpf-textbox-select-all-on-focus/

and this answer

http://stackoverflow.com/questions/1104164/wpf-datagridtemplatecolumn-am-i-missing-something

I came up with a solution. Although not strictly MVVM, I still think it falls under that design pattern. All View related stuff is still handled in the View.

Anyway - First of all. when you tab through the cells the first tab stop is the border of the cell, and the second is the actual TextBox.

Secondly, In WPF, the default behavior of the TextBox on focus is to put the cursor where it was the last time the TextBox had lost focus or if it hasn’t had focus yet, at the beginning.

The first problem I solved by by using the answer from stackoverflow:

<DataGridTemplateColumn.CellStyle>
	<Style TargetType="{x:Type DataGridCell}">
		<Setter Property="KeyboardNavigation.IsTabStop" Value="False"/>
	</Style>
</DataGridTemplateColumn.CellStyle>

The second by following Andora block. So the hole XAML DataGrid looks like this:

<DataGrid Name="TextBlockDataGrid" ItemsSource="{Binding Path=Rows}" Style="{StaticResource DefaultSettingsDataGrid}">
	<DataGrid.Columns>
		<DataGridTextColumn Binding="{Binding Text}" IsReadOnly="True"/>
		<DataGridTemplateColumn Width="*">
			<DataGridTemplateColumn.CellStyle>
				<Style TargetType="{x:Type DataGridCell}">
					<Setter Property="KeyboardNavigation.IsTabStop" Value="False"/>
				</Style>
			</DataGridTemplateColumn.CellStyle>
			<DataGridTemplateColumn.CellTemplate>
				<DataTemplate>
					<Border BorderThickness="{Binding ErrorBorderThickness}" BorderBrush="{Binding ErrorBorderBrush}">
						<TextBox Text="{Binding UserText, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" 
								 HorizontalAlignment="Right" 
								 GotKeyboardFocus="TextBox_GotKeyboardFocus" 
								 PreviewMouseDown="TextBox_PreviewMouseDown"
								 Style="{StaticResource DefaultTextBox}"/>
					</Border>
				</DataTemplate>
			</DataGridTemplateColumn.CellTemplate>
		</DataGridTemplateColumn>
	</DataGrid.Columns>
</DataGrid>

And the code-behind like this:

private void TextBox_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
	try
	{
		((TextBox)sender).SelectAll();
	}
	catch { }
}

private void TextBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
	try
	{
		// If its a triple click, select all text for the user.
		if (e.ClickCount == 3)
		{
			((TextBox)sender).SelectAll();
			return;
		}

		// Find the TextBox
		DependencyObject parent = e.OriginalSource as UIElement;
		while (parent != null && !(parent is TextBox))
		{
			parent = System.Windows.Media.VisualTreeHelper.GetParent(parent);
		}

		if (parent != null)
		{
			if (parent is TextBox)
			{
				var textBox = (TextBox)parent;
				if (!textBox.IsKeyboardFocusWithin)
				{
					// If the text box is not yet focussed, give it the focus and
					// stop further processing of this click event.
					textBox.Focus();
					e.Handled = true;
				}
			}
		}
	}
	catch { }
}

 

As always, feel free to comment, or ask.


OK this is not rocket science, but I keep forgetting.

To get the current mouse position relative to a control, System.Windows.Forms.Control has a static MousePosition property

System.Windows.Forms.Control.MousePosition

and a PointToClient method, which will take the screen coordinats as a Point as agument, and return a the client coordinates.

Eg. in a TreeView, this can be used to get the node the mouse is pointing at, as:

TreeNode editNode = treeView.GetNodeAt(treeView.PointToClient(System.Windows.Forms.Control.MousePosition));

As always, feel free to comment, or ask.


To ensure thread safety where Invoke is not implemented, can be done with a System.Windows.Threading.Dispatcher.

 

The Dispatcher should of course run on same thread as the thread safe instance, hence the Dispatcher instance should be created when the thread safe instance is constructed.

 

A little example is in order here, I guess.

Say that we have a object Foo accessed by object Bar, in Bar’ backgroundworker. This would cause a cross-thread exception

public class Foo
{
    private Object _fooBar;
    public Object FooBar
    {
        get { return _fooBar; }
        set { _fooBar = value; }
    }
    
    public Foo()
    {
    }
}

public class Bar
{
    private Foo _foo;
    public Foo Foo
    {
        get { return _foo; }
        set { _foo = value; }
    }
    
    public Bar()
    {
        this.Foo = new Foo();

        BackgroundWorker backgroundWorker = new BackgroundWorker();
        backgroundWorker.DoWork += backgroundWorker_DoWork;
    }

    void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        Foo.FooBar = NextFooBar();
    }

    private object NextFooBar()
    {
        return new Object();
    }
}

Since Foo don’t implement Invoke (or inherit from a object that does), one way to get about this would be to use a System.Windows.Threading.Dispatcher.

public class Foo
{
    private System.Windows.Threading.Dispatcher _dispatcher = null;
    private Object _fooBar;
    public Object FooBar
    {
        get { return _fooBar; }
        set { _dispatcher.Invoke(((Action<Object>))delegate(v) { internalSetFooBar(v); }, new object[] { value }); }
    }
    private void internalSetFooBar(object value)
    {
        _fooBar = value;
    }
    
    public Foo()
    {
        _dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
    }
}

public class Bar
{
    private Foo _foo;
    public Foo Foo
    {
        get { return _foo; }
        set { _foo = value; }
    }
    
    public Bar()
    {
        this.Foo = new Foo();

        BackgroundWorker backgroundWorker = new BackgroundWorker();
        backgroundWorker.DoWork += backgroundWorker_DoWork;
    }

    void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        Foo.FooBar = NextFooBar();
    }

    private object NextFooBar()
    {
        return new Object();
    }
}

As always, feel free to comment, or ask.