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.


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


WPF Controls has no ‘InvokeRequired’/’Invoke’ like WinForms, so making accessing a WPF Window/Control threadsafe can be accomplished by using the System.Windows.Threading.Dispatcher. Here are some examples. First a property: 

 

public int Progress
{
	get
	{
		if (!Dispatcher.CheckAccess())
		{
			Func<int> f = delegate () { return Progress; };
			return Dispatcher.Invoke(f);
		}

		...
	}
	set
	{
		if (!Dispatcher.CheckAccess())
		{
			Action<int> a = delegate (int progress) { Progress = progress; };
			Dispatcher.Invoke(a);
			return;
		}

		...
	}
}

 

  A void method

 

 

public void SetProgress(object obj, int progress)
{
	if (!Dispatcher.CheckAccess())
	{
		Action<object, int> a = new Action<object, int>(SetProgress);
		Dispatcher.Invoke(a, DispatcherPriority.Normal, obj, progress);
		return;
	}

	...
}

 

And lastly a method with a return value

 

 

public bool AllProcessesDone()
{
	if (!Dispatcher.CheckAccess())
	{
		Func<bool> f = new Func<bool>(AllProcessesDone);
		return Dispatcher.Invoke(f, DispatcherPriority.Normal);
	}

	...
}

 

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.


Enumerate an Enum

Published 12/2/2014 by Christian in Code syntax
Tags:

Very simple but I keep forgetring:

foreach (AnEnum e in Enum.GetValues(typeof(AnEnum)))

As always, feel free to comment, or ask.