InvalidOperationException, the calling thread should be STA because - c#

I am developping a project in order to manipulate a lot of objects with several modalities (mouse, leapmotion, touch ...). I made it using the MVVM pattern soI have several Views and ViewModels for all the components I will use. To make it easier to develop I chose to have a Canvas component in which I will manipulate Grids. Each Grid can contain any type of object (Shape, Text, Image, Documents...).
To be able to have all modalities linked to my method, I decided to build one listener per modality (1 for the mouse, 1 for the leapmotion...) and make them detect basic gestures (as Click, DoubleClick ...). All the gestures I chose to detect are associate with a method via a Dictionary. Anyway the linking is working as expected as it executes the right method. T o give an example I have the action calling in my mouse listener:
if (_leftClickCounter == 1 && _capturedLeft == false)
{
if (_dic.ContainsKey(Key.OnClick))
{
Action<object> action = _dic[Key.OnClick];
action.Invoke(null);
}
}
Where:
_dic is my dictionary
Key an enumeration of gestures (as OnClick, OnDoubleClick ...)
action the method to execute
In my example the method executed is:
public void Add(object sender)
{
ObjectModel objectModel = new ObjectModel();
ObjectView objectView = new ObjectView(objectModel);
this.objectViews.Add(objectView);
}
Where sender is just used for test purpose. It remains unused in the method. My execution stops when it tries to instanciate my ObjectView saying:
InvalidOperationException
The calling thread must be STA, because many UI components require this
My ObjectView.xaml.cs class is:
public partial class ObjectView : UserControl
{
public ObjectView(ObjectModel obj)
{
InitializeComponent();
EventLinker linker = new EventLinker(this.visualBox);
ObjectViewModel objectVM = new ObjectViewModel(obj, linker);
this.DataContext = objectVM;
}
}
And its ObjectView.xaml defining the UserControl to use is very basic:
<UserControl x:Class="AusyTouchMultimodal_v1.View.ObjectView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="visualBox" Background="Blue"/>
</UserControl>
I dont have any compilation errors, just this InvalidOperationException. Can someone explain this issue to me?
Thanks!

Try calling your actions in ui thread, like this
if (_leftClickCounter == 1 && _capturedLeft == false)
{
if (_dic.ContainsKey(Key.OnClick))
{
Action<object> action = _dic[Key.OnClick];
// action.Invoke(null);
System.Windows.Application.Current.Dispatcher.BeginInvoke( call your action )
}
}

Related

Inconsistent dispatcher.Invoke behavior

I have created a wallboard application for a service desk team, which uses WPF for front-end and the Cisco database of the phones in the back-end. The application is made of two screens that show different information, and these are displayed in the same screen and change between each other with a System.Timers.Timer.
The application is made so that if WindowA is visible, WindowB is shown and then WindowA is hidden. The moment one of the Windows becomes visible, that Window's timer become active again which resumes the database calls, while the other Window's timer becomes disabled:
private static void InterfaceChanger_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (WindowA.Visibility == Visibility.Visible)
{
WindowAEnabled = false;
ChangeVisibility(Visibility.Visible, WindowB);
WindowBEnabled = true;
WindowB_Elapsed(null, null); // force the call of the timer's callback
ChangeVisibility(Visibility.Collapsed, WindowA);
}
else
{
WindowBEnabled = false;
ChangeVisibility(Visibility.Visible, WindowA);
WindowAEnabled = true;
WindowA_Elapsed(null, null); // force the call of the timer's callback
ChangeVisibility(Visibility.Collapsed, WindowB);
}
}
private static void ChangeVisibility(Visibility visibility, Window window)
{
window.Dispatcher.Invoke(DispatcherPriority.Normal, (SendOrPostCallback)delegate
{
window.Visibility = visibility;
}, null);
}
The problem is that this works perfectly... at most 90% of the time. The problem is that sometimes, if for example WindowA's visibility is changed to Visible and WindowB's visibility is changed to Collapsed, WindowB collapses but WindowA takes 2-3 seconds to become visible, while most times WindowA becomes visible and it's not seen when WindowB collapses. This (when it doesn't work) results in the Desktop being visible instead of the application.
I originally used DispatcherPriority.Background but that resulted in the screen changer working 70-80% of the time, so I decided to change it for DispatcherPriority.Normal (DispatcherPriority.Sendresults basically in the same situation as Normal).
Questions:
Is this the normal behavior to be expected by the Dispatcher, taking into account this is running in x64 mode in a quad-core CPU?
Knowing that the queries are performed in async methods not awaited, shouldn't the Dispatcher take priority over the methods?
Is there another way (without using the Dispatcher, or using another Window property) to accomplish what I'm looking for?
This is the code used to access/start the Windows:
//WindowA:
<Application x:Class="MyNamespace.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="WindowA.xaml">
//WindowA class:
public static WindowA WindowAInstance;
public WindowA()
{
// unnecessary code hidden
WindowAInstance = this;
WindowB b = new WindowB;
}
// WindowB class
public static WindowB WindowBInstance;
public WindowB()
{
// unnecessary code hidden
WindowBInstance = this;
}
// this is the code that starts the timers
public static void StartTimersHandling()
{
Database.RemoveAgents();
InterfaceChangerTimer = new System.Timers.Timer();
InterfaceChangerTimer.Interval = ApplicationArguments.InterfaceChangerTime;
InterfaceChangerTimer.Elapsed += InterfaceChanger_Elapsed;
InterfaceChangerTimer.AutoReset = true;
InterfaceChangerTimer.Start();
WindowATimer = new System.Timers.Timer();
WindowATimer.Interval = 1000;
WindowATimer.Elapsed += WindowATimer_Elapsed;
WindowATimer.AutoReset = true;
WindowATimer.Start();
WindowBTimer = new System.Timers.Timer();
WindowBTimer.Interval = 1000;
WindowBTimer.Elapsed += WindowBTimer_Elapsed;
WindowBTimer.AutoReset = true;
WindowBTimer.Start();
}
It sounds like you're writing a kiosk application (ie. full-screen, non-interactive). If this is the case I think you would be better off having a single window and switching the views inside it, rather than switching between two separate windows. Also, you need to separate the database query work from the refreshing of the window content. Furthermore, I think it would help if the views knew nothing about each other: at the moment your first window is tightly coupled to your second, which is not really a good idea.
In my opinion, if you changed your architecture a little, a lot of the problems you are having would disappear. Here's what I would recommend:
First, just go with a single window. Create two user controls (Project > Add User Control), and move your XAML layout from your existing windows into these two new controls. Then make your main window look something like this:
<Window x:Class="StackOverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:StackOverflow"
WindowState="Maximized" WindowStyle="None">
<Grid>
<my:UserControl1 x:Name="_first" Panel.ZIndex="1" />
<my:UserControl2 Panel.ZIndex="0" />
</Grid>
<Window.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard AutoReverse="True" RepeatBehavior="Forever">
<ObjectAnimationUsingKeyFrames BeginTime="0:0:5" Duration="0:0:5"
Storyboard.TargetName="_first"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:0"
Value="{x:Static Visibility.Hidden}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
</Window>
This is a full-screen window with no chrome that contains your two user controls (essentially the contents of your existing windows). They are layered in a Grid element so that one sits on top of the other: I'm using the Panel.ZIndex property to force the first control to the top of the pile. Finally, I'm using an animation (triggered when the window loads) that toggles the visibility of one of the controls to hide it after a certain period of time. The animation is set to repeat and auto-reverse, the effect of which is to hide one of the controls, then make it visible again. You can change the Duration attribute value to control how long each control "stays" visible; it's set to 5 seconds in this example, which means a 10 second delay between switches.
The key to this working is that the first user control, when visible, must fully obscure the other user control that lies beneath it. This is easy to accomplish by setting the background colour of the control.
Your user controls can contain anything that a window would contain. Here's the example user control XAML that I used:
<UserControl x:Class="StackOverflow.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="White" Padding="40">
<TextBlock Text="{Binding Number}" FontSize="60"
TextAlignment="Center" VerticalAlignment="Top" />
</UserControl>
As you can see it's just a TextBlock element whose Text property binds to a Number property defined in the user control's code-behind. I used the same XAML for both user controls, just varying the VerticalAlignment of the text so that I could tell which control was visible at any given time.
The code-behind looks like this (it's the same for both, with the exception of the class name):
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Threading;
namespace StackOverflow
{
public partial class UserControl1 : UserControl, INotifyPropertyChanged
{
public UserControl1()
{
InitializeComponent();
DataContext = this;
_timer = new DispatcherTimer
{ Interval = TimeSpan.FromSeconds(5), IsEnabled = true };
_timer.Tick += (sender, e) => Task.Run(async () => await DoWorkAsync());
}
readonly DispatcherTimer _timer;
readonly Random _random = new Random();
public event PropertyChangedEventHandler PropertyChanged;
public int Number
{
get
{
return _number;
}
private set
{
if (_number != value)
{
_number = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Number"));
}
}
}
}
int _number;
async Task DoWorkAsync()
{
// Asynchronous code started on a thread pool thread
Console.WriteLine(GetType().Name + " starting work");
_timer.IsEnabled = false;
try
{
await Task.Delay(TimeSpan.FromSeconds(_random.Next(4, 12)));
Number++;
}
finally
{
_timer.IsEnabled = true;
}
Console.WriteLine(GetType().Name + " finished work");
}
}
}
It basically contains a single Number property (which implements INotifyPropertyChanged) that gets incremented by a "worker" method. The worker method is invoked by a timer: here, I'm using a DispatcherTimer, but as I'm not changing any UI elements directly any of the .NET timers would have done.
The worker is scheduled to run on the thread pool using Task.Run, and then runs asynchronously. I'm simulating a long-running job by waiting for a period of time with Task.Delay. This worker method would be where your database query gets called from. You can vary the gap between successive queries by setting the timer's Interval property. There's nothing to say that the gap between queries need be the same as the refresh interval of your UI (ie. the speed at which the two views are switched); indeed, as your query takes a variable amount of time, syncing the two would be tricky anyway.
Try to use Dispatcher.CurrentDispatcher instead of window.Dispatcher and BeginInvoke:
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.DataBind, new Action(() =>
{
window.Visibility = visibility;
}));
Updated
Switch your timer to DispatcherTimer:
timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(5) };
timer.Tick += (sender, args) => InterfaceChanger_Elapsed();
timer.Start();

Changing heavy UserControls in WPF Window

I have read a lot of subjects on the matter, on how to replace the displayed UserControl in WPF.
Suggestions tend towards Databinding of Content Control, and Kent Boggart answer to this question.
Anyways, the first problem is that my User Controls involve heavy graphical display, and are using common resources, not accessible at the same time.
What I mean by heavy graphics, on the OtherControl, there is a couple of videos plus a 3D model, reacting to dynamic events. The BallControl has an ellipse with TranslateAnimation, and a Margin reacting to events. I believe the whole project is a bit heavy due to the dynamic events rendering the pages.
So my solution was not ideal : I am clearing the content in the Grid, and replacing it with the needed User Control (the Grid takes the whole page).
The difference with the post above is that I would like (if possible) to keep my User Control as is.
The problem is that, these controls need to be switched very often, and after 1 or two hours using it, this Exception show up (line specified in the code):
System.ArgumentException : Specified Visual is already a child of another Visual or the root of a CompositionTarget class.
System.Windows.Threading.Dispatcher.Invoke(Action callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout)
My code is obvisously wrong and I wished a cleaner solution. What's more, I've understood that there is only one UI thread, and pre-load already constructed Controls before diplaying them was not possible.
To be more precise, a bit of code is more than welcome :
MainWindow.xaml :
<Window x:Class="Testing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Testing"
xmlns:h="http://helix-toolkit.org/wpf"
Title="MainWindow" Height="1080" Width="1920" WindowStartupLocation="CenterScreen" WindowStyle="None" MinHeight="1080" MinWidth="1920" ResizeMode="NoResize">
<Grid x:Name="Griddy">
</Grid>
MainWindow.xaml.cs :
BallControl _boule;
OtherControl _otherControl;
public MainWindow()
{
InitializeComponent();
Mouse.OverrideCursor = Cursors.None;
EventReceive.Subscribe(this);
// Instanciate both UserControls
_boule = new BallControl();
_otherControl = new OtherControl();
panelToDisplay = Turn.Ball;
Griddy.Children.Add(boule);
}
/// <summary>
/// Event coming from panels to notify which one is to be displayed
/// returning value is an Enum
/// </summary>
public void Receive<T>(T arg) where T : EventArgs
{
var casted = arg as EventArriving;
if (casted != null)
{
PanelToDisplay = casted.TurningTable;
}
}
private Turn panelToDisplay;
public Turn PanelToDisplay
{
get { return panelToDisplay; }
set
{
if (panelToDisplay != value)
{
panelToDisplay = value;
if (panelToDisplay == Turn.Other)
{
try
{
this.Griddy.Dispatcher.Invoke((Action)(() => Griddy.Children.Add(_otherControl))); //Where the exception shows up
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
try
{
this.Griddy.Dispatcher.Invoke((Action)(() => Griddy.Children.Remove(_boule)));
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
if (panelToDisplay == Turn.Ball)
{ // Bad Pattern where I remove a control to display another one.
try
{
this.Griddy.Dispatcher.Invoke((Action)(() => Griddy.Children.Add(_boule))); //Where the exception shows up
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
try
{
this.Griddy.Dispatcher.Invoke((Action)(() => Griddy.Children.Remove(_otherControl)));
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
}
}
}
To summarize a little :
How do I replace/change/add & remove a User Control from the MainWindow in a clean way?
How do I make it look like it doesn't have to load back up each time it changes, even when the controls are graphically heavy?

Positioning a dialog window in WPF

I have a command button that launches a separate window. I'm using the MVVM Cross architecture, and so the window is fed through a command in the ViewModel. The way that I've done this is a register a service in the UI layer that displays a window, and then in the command, to resolve that service and display the window:
public static void ShowMyWindow()
{
IShowMyWindowService myService = Mvx.Resolve<IShowMyWindowService>();
myService.ShowWindow();
}
Then, in the service (which is in the UI layer):
public void ShowWindow()
{
Window myWindow = new Window();
myWindow.Content = new MyUserControl();
Application.Current.Dispatcher.Invoke(() =>
{
myWindow.Owner = Application.Current.MainWindow;
// Need to set x, y to the position of the button that invoked the command
myWindow.Left = x;
myWindow.Top = y;
myWindow.ShowDialog();
});
}
The problem that I have is that I want to show the new dialog in a position relative to the position of the command button that launched it. Having done a bit of research, it looks like I need the FrameworkElement.PointToScreen() method, although what I can't determine is how to access this information without breaking the separation of concern.
Am I going about this the correct way, or is there an easier way? If i am, then how can I pas the framework element through the command?
You should have a method / command, as such:
public static void ShowMyWindow(WindowAbsoluteLocationPosition pos)
{
IShowMyWindowService myService = Mvx.Resolve<IShowMyWindowService>();
myService.ShowWindow(pos);
}
If that is done, you should use CommandParameter with Converter:
<Button Command="{Binding YourCommand}"
CommandParameter="{Binding ElementName=YourWindow,
Converter={StaticResource yourConverter}}" />
which basically can turn YourWindow into the correct WindowAbsoluteLocationPosition (user-defined) structure that is passed to your viewModel. Notice; you should also pass your button to the converter, in order to have better "reusability".
In this scenario, I would propose the following:
Create another method under the same name which has an additional parameter which represents the parent window:
public void ShowWindow(Window parentWindow)
{
//Access the parentWindow location properties to work out a relative position to show the dialog
}
You will need to also overload the ShowMyWindow method in the service with the same parameter to call this new ShowWindow method.

WPF - Binding events to class methods of Item in ItemControl

I'm a bit new to WPF/XAML (though I've learnt C#) and would really appreciate any help for my question. I did look around other posts and google for a while but I can't seem to find a satisfactory or detailed answer to get me going on with my project. Please look below for details. Thanks you in advance!
Objective
I have a class called Tile that consists of a few properties and an event handler.
I also have an ItemControl that has a button (as by the DataTemplate), and whose ItemSource is a collection of Tiles.
Now, I want to bind the "Click" event of the Button so as to invoke the Event Handler method defined in the class Tile.
In other words when I click the button of any item in the ItemControl, the method handler of the corresponding Tile instance (from the collection) must be invoked. How would I tackle this problem?
Below is the entire code, simplified to avoid distraction:
XAML
<Window x:Class="SampleWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="300">
<!-- Make a ItemControl for "Tile"s. -->
<ItemsControl x:Name="TileList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- Wire the click event of this Button
to event handler in the Tile class. -->
<Button Content="Show"></Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
CODE-BEHIND
namespace SampleWPF
{
public partial class MainWindow : Window
{
ObservableCollection<Tile> tiles;
public MainWindow()
{
InitializeComponent();
// Adding some sample data for testing.
tiles = new ObservableCollection<Tile>();
tiles.Add(new Tile("Item 1"));
tiles.Add(new Tile("Item 2"));
TileList.ItemsSource = tiles;
}
}
public class Tile : INotifyPropertyChanged
{
public string Data
{ /* Accessors and PropertyNotifiers */ }
public Tile(string data)
{ /* Initializing and assigning "Data" */ }
// INotifyPropertyChanged implementation...
// { ... }
// This event handler should be bound to the Button's "Click" event
// in the DataTemplate of the Item.
public void ShowButton_Click(object sender, EventArgs e)
{
MessageBox.Show("Viewing item from: " + this.Data);
}
}
}
Hence, if I click the first "Show" button, the output should be "Viewing item from: Item 1" and if I click the second "Show" Button, the output should be "Viewing item from: Item 2".
So what is the recommended/efficient way to do this? Is my code inappropriate for this requirement?
Event handlers are the wrong approach - use Commands and more importantly MVVM.
As I can see that you are new (and probably from a WinForms or ASP.NET background) you should read this blog to understand how your thinking needs to change - this is the most important part to understand before tackling WPF: http://rachel53461.wordpress.com/2012/10/12/switching-from-winforms-to-wpfmvvm/
You should also read Kent Boogart's blog on how MVVM works from base principles: http://kentb.blogspot.co.uk/2009/03/view-models-pocos-versus.html
Let me start with some basics:
Don't assign itemsource in codeBehind - use Binding like this:
<Controll ItemSource="{Binding MyObservableCollection}"/>
There are many ways You can achieve this. I think that using this.Data is not the best solution for this.
For example if Your tail have ID or something You can assign this id to button CommandParameter like below
<Button CommanParameter="{Binding Path=ID}" Click="ShowButton_Click"/>
And then in Your button_click event u can 'catch' this like this:
public void ShowButton_Click(object sender, EventArgs e)
{
int ID = int.Parse(((Button)sender).CommandParameter.ToString());
}
EDIT
To use this binding You need to set DataContext. You can do this in ctor like this:
public MainWindow()
{
InitializeComponent();
// Adding some sample data for testing.
tiles = new ObservableCollection<Tile>();
tiles.Add(new Tile("Item 1"));
tiles.Add(new Tile("Item 2"));
// below You are setting a datacontext of a MainWindow to itself
this.DataContext = this;
}
ANOTHER EDIT
Let's assume Your tail class have property called ID. If You bound this ID to Button.CommandParameter You can later retrieve the tile with linq like this:
public void ShowButton_click(object sender, EventArgs e)
{
int MyId = int.Parse(((Button)sender).CommandParameter.ToString());
Tile TileIWasSearchingFor = (from t in tiles where t.ID == MyId select t).First();
// do something with tile You found
}
Well since my requirement was rather "simple", I've managed a work around, avoiding commands. Thanks to the answer here by MajkeloDev: https://stackoverflow.com/a/27419974/3998255 for guidance.
This is the final event handler:
public void ShowButton_Click(object sender, EventArgs e)
{
Tile requestingTile = (sender as Button).DataContext as Tile;
if(requestingTile != null)
MessageBox.Show("Viewing item from: " + this.Data);
// Or whatever else you want to do with the object...
}
Also, adding the ItemSource as a XAML attribute:
<ItemsControl x:Name="TileList" ItemsSource="{Binding tiles}">
And setting DataContext in constructor of MainWindow:
public MainWindow()
{
this.DataContext = this;
// Whatever else you want to do...
}
Well it works as required.

Calling UIElement Command from code behind

I am trying to execute a bound command from my code behind utilizing the UiElement. button.Command.Execute(button.CommandParameter)
However, at this point the Command property of the button is null. simultaneously when I check the command in my View Model the property is set. The only diagnosis I can come up with is that until the window is actually visible the command is not bound to the command property of the button. I feel like may I'm missing a step somewhere or my implementation is not sound. below is some snipits of the code, please let me know if you need more.
Window constructor:
public PlottingViewModel ViewModel { get; set; }
public PlottingGUI()
{
InitializeComponent();
DataContext = (ViewModel = new PlottingViewModel());
_setDefaultSelections();
}
IList<RadioButton> buttons;
Setting default selections:
private void _setDefaultSelections()
{
buttons = new List<RadioButton>();
_getRadioButtons(this);
foreach (var setting in ViewModel.Settings.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
var settingValue = setting.GetValue(ViewModel.Settings);
var button = buttons.FirstOrDefault(btn => btn.Content.Equals(settingValue)
|| ((string)btn.CommandParameter).Equals(settingValue));
if (button == null)
continue;
button.IsChecked = true;
// NullReference here
// button.Command.Execute(button.CommandParameter);
}
}
one of the RadioButtons XAML:
<RadioButton Content="None"
Grid.Row="0"
Command="{Binding StampedCommand}"
CommandParameter="None"
Foreground="WhiteSmoke"/>
I feel, the only way i may be able to successfully complete this task is to execute the command directly from my viewmodel. (Which i don't want to do)
Thanks for reading..
To sum up comments at the point when you're calling _setDefaultSelections() bindings have not been updated yet, hence Command is still null, so you have to wait until everything is loaded. You can call _setDefaultSelections during Loaded event
Occurs when the element is laid out, rendered, and ready for interaction.

Categories

Resources