Tag Archives: WPF

Windows Forms or WPF UI updates from a different thread #coding #programming #csharp

OK, we’ve all wanted to update the UI from a different thread at some point, it is annoying, that damn exception you get…
System.InvalidOperationException
{“Cross-thread operation not valid: Control ‘listBox1’ accessed from a thread other than the thread it was created on.”}

I tend to come back to the same approach time and time again, there may be others, but here’s an example I knocked up for a friend because this is easier to read than explain over the phone, upon clicking button1 I want to add some text to listBox1 without locking up the UI:

        private static void LogToList(SynchronizationContext ctx, ListBox listBox, String text)
        {
            ctx.Post((t) => { listBox.Items.Add(t); }, text);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Task.Factory.StartNew((ctx) =>
            {
                for (int i = 0; i < 10; i++)
                {
                    LogToList((SynchronizationContext)ctx, listBox1, DateTime.Now.ToString());
                    Thread.Sleep(1000);
                }
            }, (Object)SynchronizationContext.Current);
        }

For those who haven’t used it before, Task was introduced in .NET 4 under what is known as the Task Parallel Library (TPL), from what I can tell it is seen as the replacement for BackgroundWorker, but there’s no reason why you can’t do the same with a BackgroundWorker as we did before .NET 4.

In WPF I’ve previously also used System.Windows.Threading.Dispatcher.BeginInvoke in a similar-ish way. To get the Dispatcher instance, what you want is System.Windows.Application.Current.Dispatcher.

This also reminds me that if you’re doing a lot of logging / updating of a control then setting UndoLimit to zero is a good thing if you don’t want to leak / consume what can be vast amounts of memory for an undo buffer you don’t want or need.

Advertisements

WPF Window (or do I mean View) Navigation (or do I mean switching, opening, closing…) part 3 #wpf

Following on from my post WPF Window (or do I mean View) Navigation (or do I mean switching, opening, closing…) part 2 I have finally put together the view switching part, here goes…

First I added two User Control (WPF) items (for now I’m going to keep these in a new folder called SubViews) and a MvvmViewModel (WPF) for each:
Nav\Views\SubViews\UC1.xaml + .cs
Nav\Views\SubViews\UC2.xaml + .cs
Nav\ViewModels\UC1ViewModel.cs
Nav\ViewModels\UC2ViewModel.cs

Next I added some dependency properties to my ViewModels using the mvvminpc snippet.
Starting with UC1ViewModel, I added Val1 of type String:

        /// <summary>
        /// The <see cref="Val1" /> property's name.
        /// </summary>
        public const string Val1PropertyName = "Val1";

        private string _val1 = "";

        /// <summary>
        /// Sets and gets the Val1 property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string Val1
        {
            get
            {
                return _val1;
            }

            set
            {
                if (_val1 == value)
                {
                    return;
                }

                RaisePropertyChanging(Val1PropertyName);
                _val1 = value;
                RaisePropertyChanged(Val1PropertyName);
            }
        }

Similarly I added Val2 to UC2ViewModel.
These are just something to bind to and display.

Next I added a dependency property of type UserControl and called UC to AnotherViewModel, I will bind to this and change this to swap the UserControl that is displayed:

        /// <summary>
        /// The <see cref="UC" /> property's name.
        /// </summary>
        public const string UCPropertyName = "UC";

        private UserControl _uc = null;

        /// <summary>
        /// Sets and gets the UC property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public UserControl UC
        {
            get
            {
                return _uc;
            }

            set
            {
                if (_uc == value)
                {
                    return;
                }

                RaisePropertyChanging(UCPropertyName);
                _uc = value;
                RaisePropertyChanged(UCPropertyName);
            }
        }

Of course next I needed the UserControl instances and commands to swap them, I added them like so:

        private UC1 _uc1;
        private UC2 _uc2;
        public RelayCommand Swap1a { get; set; }
        public RelayCommand Swap1b { get; set; }

And in constructor:

            _uc1 = new UC1() { DataContext = new UC1ViewModel() { Val1 = "AVM uc 1a" } };
            _uc2 = new UC2() { DataContext = new UC2ViewModel() { Val2 = "AVM uc 1b" } };

            Swap1a = new RelayCommand(() => { UC = _uc1; });
            Swap1b = new RelayCommand(() => { UC = _uc2; });

Next up was to add code to ViewModelLocator to add design and blendability.
Using the mvvmlocatorproperty snippet I added a property for use when designing the UserControls, notice I don’t use the IoC container in this case:

        /// <summary>
        /// Gets the UC1VMDesignTime property.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
            "CA1822:MarkMembersAsStatic",
            Justification = "This non-static member is needed for data binding purposes.")]
        public UC1ViewModel UC1VMDesignTime
        {
            get
            {
                if (ViewModelBase.IsInDesignModeStatic)
                {
                    return new UC1ViewModel() { Val1 = "Designing val1..." };
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Gets the UC2VMDesignTime property.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
            "CA1822:MarkMembersAsStatic",
            Justification = "This non-static member is needed for data binding purposes.")]
        public UC2ViewModel UC2VMDesignTime
        {
            get
            {
                if (ViewModelBase.IsInDesignModeStatic)
                {
                    return new UC2ViewModel() { Val2 = "Designing val2..." };
                }
                else
                {
                    return null;
                }
            }
        }

I also changed Another so that at design time there was data in the UC:

        /// <summary>
        /// Gets the Another property.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
            "CA1822:MarkMembersAsStatic",
            Justification = "This non-static member is needed for data binding purposes.")]
        public AnotherViewModel Another
        {
            get
            {
                AnotherViewModel avm = ServiceLocator.Current.GetInstance<AnotherViewModel>();

                if (ViewModelBase.IsInDesignModeStatic)
                {
                    avm.UC = new UC1() { DataContext = UC1VMDesignTime };
                }

                return avm;
            }
        }

Finally I wired it all up in the xaml files.
UC1.xaml:

d:DataContext="{Binding UC1VMDesignTime, Source={StaticResource Locator}}"
....
<StackPanel>
	    	<Label Content="uc1" Height="28" Margin="8,10,0,0" x:Name="label1" VerticalAlignment="Top" HorizontalAlignment="Left" Width="36.803" />
	    	<TextBox x:Name="txt1" Margin="83,0,111,0" TextWrapping="Wrap" Text="{Binding Val1}" VerticalAlignment="Top" Height="49.96"/>
	    	<Label x:Name="lbl1" Content="{Binding Val1}" Margin="68,0,71.007,58.04" VerticalAlignment="Bottom" Height="44.96"/>
		</StackPanel>

UC2.xaml:

d:DataContext="{Binding UC2VMDesignTime, Source={StaticResource Locator}}"
....
<StackPanel>
    		<Label Content="uc2" HorizontalAlignment="Left" Height="35.96" Width="54" />
    		<Label Content="{Binding Val2}" Height="39.96" Margin="78,0,40,0" />
        </StackPanel>

AnotherWindow.xaml:

        <ContentControl x:Name="ContentControl" Content="{Binding UC}" Margin="177.5,82,110.5,0" Height="255" Width="464" VerticalAlignment="Top">
        </ContentControl>
        <Button Content="Swap to 1a" HorizontalAlignment="Left" Margin="0,0,0,124.04" VerticalAlignment="Bottom" Width="75" Command="{Binding Swap1a}"/>
        <Button Content="Swap to 1b" HorizontalAlignment="Left" Margin="0,0,0,98.08" VerticalAlignment="Bottom" Width="75" Command="{Binding Swap1b}"/>

Note that d:DataContext is ignored except at design time, so we have the ability to chose a design time model and I have coded those models so that they are null when not in design time anyway. I really like how that bit turned out, it’s really nice to have something you can design later (or pass onto a designer), there is nothing worse than a broken view that you can’t use in VisualStudio or Blend.

WPF Window (or do I mean View) Navigation (or do I mean switching, opening, closing…) part 2 #wpf

Following on from my post WPF Window (or do I mean View) Navigation (or do I mean switching, opening, closing…) I thought I would quickly cover a slight variation on the open and close view I was doing. Essentially you might want to merely temporarily show a different view or quickly switch between views without recreating it.

To do this I updated my button and added a new button like so:

<Button Content="Switch" HorizontalAlignment="Left" Margin="0,0,0,160" VerticalAlignment="Bottom" Width="75" Command="{Binding Switch}"/>
        <Button Content="Switch Hide" HorizontalAlignment="Left" Margin="0,0,0,190.04" VerticalAlignment="Bottom" Width="75" Command="{Binding SwitchHide}"/>

The old button is now called Switch and the new button SwitchHide. I bound SwitchHide button to the SwitchHide command which is defined like so:

SwitchHide = new RelayCommand(() => { ((App)App.Current).SwitchHideWindow(); });
public RelayCommand SwitchHide { get; set; }

I then wrote the code for SwitchHideWindow in App.xaml.cs and updated the code for SwitchWindow like so:

        internal void SwitchWindow()
        {
            // If we have extra windows the close any hidden ones
            if (Windows.Count > 1)
            {
                foreach (Window w in Windows)
                {
                    if (!w.IsVisible)
                    {
                        w.Close();
                        w.DataContext = null; // Ensure w no longer has a binding to the ViewModel, so we can reuse it at our leisure
                    }
                }
            }

            Window w0 = Windows[0];
            Window w1;

            if (w0 is MainWindow)
            {
                w1 = new AnotherWindow();
            }
            else
            {
                w1 = new MainWindow();
            }

            w1.Show();
            w0.Close();
            w0.DataContext = null; // Ensure w0 no longer has a binding to the ViewModel, so we can reuse it at our leisure
        }

        internal void SwitchHideWindow()
        {
            Window w0 = Windows[0];
            Window w1;

            // Ensure we have two windows
            if (Windows.Count < 2)
            {
                if (w0 is MainWindow)
                {
                    w1 = new AnotherWindow();
                }
                else
                {
                    w1 = new MainWindow();
                }
            }
            else
            {
                w1 = Windows[1];
            }

            if (w1.IsVisible)
            {
                w0.Show();
                w1.Hide();
            }
            else
            {
                w1.Show();
                w0.Hide();
            }
        }

If you set breakpoints in the MainWindow and AnotherWindow constructors you will see they are not called once there are two windows and we SwitchHide between them.

Sure, we have more code now, but nothing difficult, just some demo code to show the principle.

Next time I hope to cover the swapping of controls in a view, we’ll see.

WPF Window (or do I mean View) Navigation (or do I mean switching, opening, closing…) #wpf

The motivation for this post is to explore WPF view navigation, since my most recent work had a nice (custom) framework in place where I never had to do any of the navigation I thought it was time to revisit this subject as I found myself asking a basic question, “how do I switch from this View to that View?”.

Starting at the start, I used:
– Microsoft Windows 7 Enterprise
– Microsoft Visual Studio 2010 Ultimate (Service Pack 1)
– Microsoft Expression Blend 4
– The MVVM Light Toolkit

I created an MvvmLight (WPF4) project using the Visual Studio template (installed by the toolkit), I compiled and then ran it. Great, a simple MVVM app up and running, but no app of any complexity has a single View, so I next wanted to implement another view (erm, window?) then some navigation between them.

I added a new MvvmView (WPF) and a MvvmViewModel (WPF).
Here I noticed the views are in the root level, so I did some housekeeping, giving me the following structure:
Nav (my project name)
Nav\Design\DesignDataService.cs
Nav\Models\DataItem.cs
Nav\Models\DataService.cs
Nav\Models\IDataService.cs
Nav\Skins\MainSkin.xaml
Nav\ViewModels\AnotherViewModel.cs (mine)
Nav\ViewModels\MainViewModel.cs
Nav\ViewModels\ViewModelLocator.cs
Nav\Views\AnotherWindow.xaml + .cs (mine)
Nav\Views\MainWindow.xaml + .cs
Nav\App.xaml

The AnotherViewModel was not yet supported by the Locator, so I added the following to ViewModelLocator.cs using the mvvmlocatorproperty snippet:

        /// <summary>
        /// Gets the Another property.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
            "CA1822:MarkMembersAsStatic",
            Justification = "This non-static member is needed for data binding purposes.")]
        public AnotherViewModel Another
        {
            get
            {
                return ServiceLocator.Current.GetInstance<AnotherViewModel>();
            }
        }

And this bit into the static constructor:

SimpleIoc.Default.Register<AnotherViewModel>();

Next I wired up the Binding in the AnotherWindow.xaml to use AnotherViewModel:

DataContext="{Binding Another, Source={StaticResource Locator}}"

So the view I created is now using the correct ViewModel, which is always good. And I wanted the same skin as MainWindow, so I also added the following:

    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="../Skins/MainSkin.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>

The last few steps will be required each time a new View and ViewModel is added.

Next (more to illustrate how this thing holds together than anything else) I re-wired the App.xaml to start AnotherWindow at first:

StartupUri="Views/AnotherWindow.xaml"

This is my way of showing MainWindow is nothing special. Or is it? Well it does have the extra code in MainWindow.xaml.cs:

Closing += (s, e) => ViewModelLocator.Cleanup();

I don’t need this, nor do I care about cleanup in this case as the ViewModelLocator will exist till the bitter end, so I commented it out.
I then ran it up, a nice blank window this time, as planned.

I decided I wanted my App to handle the window switching, so I wrote a little method to do this for me (in App.xaml.cs):

        private int selectedWindow = 0;

        internal void SwitchWindow()
        {
            Window cw = Windows[0];
            Window nw;
            if (selectedWindow == 0)
            {
                nw = new MainWindow();
                selectedWindow = 1;
            }
            else
            {
                nw = new AnotherWindow();
                selectedWindow = 0;
            }
            nw.Show();
            cw.Close();
        }

You can bind a ViewModel command to a button easily enough, so I added a RelayCommand to AnotherViewModel that called the SwitchWindow:

        /// <summary>
        /// Initializes a new instance of the AnotherViewModel class.
        /// </summary>
        public AnotherViewModel()
        {
            Switch = new RelayCommand(() => { ((App)App.Current).SwitchWindow(); });
        }

        public RelayCommand Switch { get; set; }

Sure I’m casting App.Current to App here, I could have created SwitchWindow as a static member of App, this would remove the need for that, but I didn’t.
I rebuilt the solution then (in blend) I added a button to AnotherWindow.xaml and drag and dropped the Switch RelayCommand onto the button, this added the following xaml:

<Button Content="Button" HorizontalAlignment="Left" Margin="272,0,0,190.04" VerticalAlignment="Bottom" Width="75" Command="{Binding Switch}"/>

I rebuilt, ran and tested I could switch one way, then I needed to add the same code to MainViewModel and button on MainWindow.xaml. On this occasion copy and paste was fine to test it and it worked going both ways as expected, the Switch code could of course be moved into a new ViewModel base class in future.
So what have I achieved here, not a lot, but in sticking the switch code into the App class I have removed any inter ViewModel dependency I would otherwise introduce, I guess I am looking at App as being my navigation controller at the moment, but this logic could of course be moved elsewhere.

Next up is to revisit view switching (within the same window), from my ASP.NET background this to me is swapping user controls. I hope to cover this in a follow up blog post.

WPF simulate mouse click in code behind

Let’s say there is an event captured on a control and you want to simulate a click of a button to fire whatever that button is bound to do, there are a few ways of doing this, one that I’ve used before is using an AutomationPeer:

AutomationPeer button1AP = UIElementAutomationPeer.CreatePeerForElement(button1);
(button1AP.GetPattern(PatternInterface.Invoke) as IInvokeProvider).Invoke();

Another option is to use:

button1.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));