Change WPF mainwindow label from another class and separate thread - wpf

Im working on a WPF application. I have a label called "Status_label" in MainWindow.xaml. and I want to change its content from a different class (signIn.cs).
Normally I'm able to do this
var mainWin = Application.Current.Windows.Cast<Window>().FirstOrDefault(window => window is MainWindow) as MainWindow;
mainWin.status_lable.Content = "Irantha signed in";
But my problem is,when I'm trying to access it via different thread in signIn.cs class, it gives an error:
The calling thread cannot access this object because a different thread owns it.
Can I solve this by using Dispatcher.Invoke(new Action(() =>{.......... or something else?
EDIT:
I'm gonna call this label change action from different class as-well-as separate thread
MainWindow.xaml
<Label HorizontalAlignment="Left" Margin="14,312,0,0" Name="status_lable" Width="361"/>
SignIn.cs
internal void getStudentAttendence()
{
Thread captureFingerPrints = new Thread(startCapturing);
captureFingerPrints.Start();
}
void mySeparateThreadMethod()
{
var mainWin = Application.Current.Windows.Cast<Window>().FirstOrDefault(window => window is MainWindow) as MainWindow;
mainWin.status_lable.Dispatcher.Invoke(new Action(()=> mainWin.status_lable.Content ="Irantha signed in"));
}
line var mainWin return errorThe calling thread cannot access this object because a different thread owns it.
Please guide me,
Thank you

I resolved my question, hope somebody will need this. But don't know whether this is the optimized way.
In my mainWindow.xaml.cs :
public MainWindow()
{
main = this;
}
internal static MainWindow main;
internal string Status
{
get { return status_lable.Content.ToString(); }
set { Dispatcher.Invoke(new Action(() => { status_lable.Content = value; })); }
}
from my SignIn.cs class
MainWindow.main.Status = "Irantha has signed in successfully";
This works fine for me.
You can find more details from here, Change WPF window label content from another class and separate thread
cheers!!

try below snippet:
status_lable.Dispatcher.Invoke(...)

Thanks to the answers, they led me in the right direction. I ended up with this simple solution:
public partial class MainWindow : Window
{
public static MainWindow main;
public MainWindow()
{
InitializeComponent();
main = this;
}
}
Then in my eventhandler in another class that runs in a different thred:
internal static void pipeServer_MessageReceived(object sender, MessageReceivedEventArgs e)
{
MainWindow.main.Dispatcher.Invoke(new Action(delegate()
{
MainWindow.main.WindowState = WindowState.Normal;
}));
}
This to show the minimized window when i message is received via a namedPipeline.

Thank you! I wound up with a slightly different solution, but you definitely pointed me in the right direction with your answer.
For my application, I have a lot of controls in main, and most of the method calls on main were occurring from within the scope of main, so it was simpler to use the default { get; set } within MainWindow.xaml.cs (or to just define the controls in XAML).
In my parent window's code-behind, I launch the MainWindow in a separate thread like this (simplified example). The key is to define main globally, even though it is instantiated inside of Window_Loaded():
public ParentWindow()
{
InitializeComponent();
}
MainWindow main;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Thread otherThread = new Thread(() =>
{
main = new MainWindow();
main.Show();
main.Closed += (sender2, e2) =>
main.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
});
otherThread.SetApartmentState(ApartmentState.STA);
otherThread.Start();
}
Then in my MainWindow code-behind, I just interact with the controls as though it is a simple single-threaded application (there is no control of the parent thread from the child thread in my case). I can, however, control main from the parent thread like this:
private void button_Click(object sender, RoutedEventArgs e)
{
main.Dispatcher.Invoke(new Action(delegate ()
{
main.myControl.myMethod();
}));
}
By doing it this way, I avoid the complexity of defining everything in code-behind and using the dispatcher from within the code-behind of MainWindow.xaml.cs. There are only a few spots in my application where I modify main from the parent window, so this was simpler for me, but your approach seems equally valid. Thanks again!

Simple trick without use Dispatcher.Invoke: In your Window class put this:
public partial class MyWindow: Window
{
public static MyWindow mywin;
[...]
public MyWindow()
{
InitializeComponent();
mywin = this;
[...]
}
[...]
}
Next in your second class to call the properties you need to add your window name + the label that you've assigned. This is an example:
internal class MySecondClass
{
internal void ChangeAValue()
{
MyWindow.mywin.ATextBox.Text = "Some text"; // I'm changing for example the text for a textbox in MyWindow.
}
}

Related

(WPF) Does .Close() method releases the window instance?

I'm creating a new window in On_Click method. First I tried this;
public partial class MainWindow : Window
{
CustomerOperations customerOperationsWindow;
public MainWindow()
{
customerOperationsWindow = new CustomerOperations();
InitializeComponent();
}
private void btnCustomer_Click(object sender, RoutedEventArgs e)
{
customerOperationsWindow.Owner = this;
customerOperationsWindow.Show();
}
}
It's not working so I started creating the window instance every time the user clicks on the Customers button. And I used the following codes.
private void btnCustomer_Click(object sender, RoutedEventArgs e)
{
CustomerOperations customerOperationsWindow = new CustomerOperations();
customerOperationsWindow.Owner = this;
customerOperationsWindow.Show();
}
In the new window, If user clicks to Main button, I want to navigate to main window.
private void btnMain_Click(object sender, RoutedEventArgs e)
{
this.Close();
this.Owner.Show();
}
First question: Does this.Close() releases the window instance?
Second question: Is this usage correct?
What do you think is the best practice?
Thank you all.
Window.Close() will dispose all resources allocated by the instance. That's why you cannot show it again once it was closed.
If you want to reuse the same Window instance, you should cancel the closing procedure to prevent disposal of internal resources and collapse the Window instead (by setting Window.Visibility to Visibility.Collapsed - Visibility.Collapsed is also the default value of an instantiated Window before Window.Show() is called).
Alternatively hide the Window by calling Window.Hide() (which will set the Visibility to Visibility.Hidden) instead of Window.Close().
Calling Window.Show will also set the window's visibility to Visibility.Visible.
As a matter of fact, showing a Window by setting Window.Visibility is the asynchronous version of Window.Show().
Generally, you switch between Window instances by using the Window.Activate method. Calling Window.Show on a Window that is currently showing/visible, does nothing.
public partial class MainWindow : Window
{
CustomerOperations CustomerOperationsWindow { get; }
public MainWindow()
{
InitializeComponent();
this.CustomerOperationsWindow = new CustomerOperations();
// Consider to move this logic to CustomerOperations class,
// where you can override the OnClosing method instead of subscribing to the event
this.CustomerOperationsWindow.Closing += CollapseWindow_OnClosing;
}
// Cancel close to prevent disposal and collapse Window instead
private void CollapseWindow_OnClosing(object sender, CancelEventArgs e)
{
e.Cancel = true;
this.CustomerOperationsWindow.Visibility = Visibility.Collapsed;
this.CustomerOperationsWindow.Owner.Activate();
}
private void btnCustomer_Click(object sender, RoutedEventArgs e)
{
this.CustomerOperationsWindow.Owner = this;
// Calling Show will set the Visibility to Visibility.Visible
this.CustomerOperationsWindow.Show();
}
}
Creating a Window instance allocates unmanaged resources. If this happens very frequently, you will keep the garbage collector busy. From a performance point of view you may want to avoid it and prefer to reuse the same instance.
In a common scenario this is not necessary. But since Window exposes a Hide() method, you may consider to use it instead of Close().
If you want to switch to the parent window, you can use the code this.Owner.Activate(); and if you want to close the current window, first this.Owner.Activate(); and then this.Close();.
When you enter this.Close(), the compiler does not execute the following lines after reaching it. And when a sample window still exists there is no need to recreate it
private void btnMain_Click(object sender, RoutedEventArgs e)
{
this.Owner.Activate();
this.Close();
}

Get ClassName of a Window from Window Reference

I have a Window partial class (WPF Window) like:
public partial class MyWindow : Window
{
// this is just a WPF window
// I have in XAML Closing event like Closing="Window_Closing"
// and here is the event definition
public void Window_Closing(object sender, CancelEventArgs e)
{
SaveWindowState(this); // just passes reference to itself
}
}
In another assembly, I have logic which receives reference passed in above like this
public static void SaveWindowState(Window window)
{
// Since I can call this from many windows, I need a way to get
// the class name of my window in here. Basically, for MyWindow
// above, I need to get "MyWindow" and for other windows, I need
// to get thier class name from the passed in "window" parameter.
}
How do I get the actual class name for the passed in Window?
Simply window.GetType().Name?

Opening a new dialog using WPF with MVVM

I am currently using MVVM (Light) to build an application with WPF. However, in a few cases I must open a new dialog (also WPF) when the user clicks a button. However, this is being a tough fight.
Here is how I am doing it:
private void _ShowItemDialog(Item item)
{
var itemVM = new ItemViewModel();
itemVM.CurrentItem = item ?? new Item();
itemVM.Load();
var itemView = new View.ItemView() { DataContext = itemVM };
if (itemView.ShowDialog() == true)
{
if (item == null)
{
itemList.Add(itemVM.CurrentItem);
}
}
itemVM.Cleanup();
}
And the itemView XAML there is no binding to the DataContext, otherwise two different instances of the ViewModel would be created.
Inside the Window tag. To have the result at ShowDialog, I use the DialogCloser code:
public static class DialogCloser
{
public static readonly DependencyProperty DialogResultProperty =
DependencyProperty.RegisterAttached(
"DialogResult",
typeof(bool?),
typeof(DialogCloser),
new PropertyMetadata(DialogResultChanged));
private static void DialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window != null)
window.DialogResult = e.NewValue as bool?;
}
public static void SetDialogResult(Window target, bool? value)
{
target.SetValue(DialogResultProperty, value);
}
}
In the ItemView, this is declared inside Window tag as follows:
my:DialogCloser.DialogResult="{Binding DialogResult}"
And when the dialog is closed, the closing event sets DialogResult to true or false.
This works perfectly for the first time the screen is opened, but it is not possible to open the dialog again after it is closed.
I would like to know if you have any better ideas for opening the dialog, and why this code does not work.
Thanks!
EDIT:
I have already fixed the code. What I need to do is create a new ViewModel and attach it to the DataContext every time the dialog is opened. Moreover, I had to remove the DataContext binding from XAML. Please check the code changes above.
With these changes I have found out that it is not possible to use the ViewModel from ViewModelLocator because it is a "singleton" and not a new instance at each new window. Therefore, the DialogResult held the last value and if I tried to change its value back to null (as it is when the ViewModel is initialized) an exception is thrown. Do you have any clues of why this happens? It would be very good for me to use the ViewModel from ViewModelLocator, since it would keep the same strategy throughout the system.
Thank you!
I do that by implementing static XxxxInteraction classes that have methods called for example NewUser(); That methods opens the Dialogs and do some work. In my ViewModel I call the XxxxInteraction classes via commands.
The efforts of that way of implementing is, that you can easely modify the methods in the static Interaction classes for using UnitTests.
public static class UserInteractions
{
public static User NewUser()
{
var userDialog = new NewUserDialog();
If(userDialog.ShowDialog() != true) return null;
var user = new User();
user.Name = userDialog.Name;
user.Age = userDialog.Age;
return user;
}
}
public class MyViewModel
{
...
public void NewUserCommandExecute()
{
var newUser = UserInteractions.NewUser();
if(newUser == null) return;
//Do some with new created user
}
}
NewUserDialog is a normal Window that is bound to a ViewModel too.
I think this is a good way of implementing dialogs for the mvvm pattern.
i've done this a while ago, i use a dialog service and call this service in my viewmodel. take a look.
EDIT: btw, thats all you have to do in your viewmodel
var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);

Using same ResourceDictionary object in multiple controls

I have a scenario where I parse the XAML of the resource dictionary at runtime. Like following:
var parsedResourceDictionary = XamlReader.Parse(xaml) as ResourceDictionary;
This all happens inside a custom resource dictionary (derived from ResourceDictionary as base class). After parsing, I call
MergedDictionaries.Add(parsedResourceDictionary);
Since parsing the XAML takes quite some time I want to cache the parsers output and just call the add method on the MergedDictionary field.
Now, my question is if it is possible to keep a reference to this parsedResourceDictionary and add it later.
thanks
Yes. Read it once, put it in a variable somewhere and use that in future instead of reading it again. Did you try it?
I just made a small testing app where I created a ResourceDictionary from an embedded XAML:
public partial class MainWindow : Window
{
public static ResourceDictionary CachedResourceDictionary;
public MainWindow()
{
if (CachedResourceDictionary == null)
{
CachedResourceDictionary = new ResourceDictionary
{
Source =
new Uri("/ResourceDictionaryCache;component/Dictionary1.xaml",
UriKind.RelativeOrAbsolute)
};
}
Resources.MergedDictionaries.Add(CachedResourceDictionary);
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var toOpen = new MainWindow();
toOpen.Show();
}
}
In the Button_Click event I just created a new instance of the MainWindow which then

WPF usercontrol showdialog problem

There is a MainWindow, a usercontrol which is located in my MainWindow and a OtherForm which I am going to show from usercontrol. I sent OtherForm as parameter from MainWindow to usercontrol. And in usercontrol I am calling OtherForm.showdialog. When I show it the second time, I am getting "Cannot set Visibility or call Show, ShowDialog, or WindowInteropHelper.EnsureHandle after a Window has closed" problem.
Code
In MainWindow class
void Example()
{
usercontrol.Load(new Otherform{ variable= 1 });
}
In Usercontrol class
private Window _form;
public void Load(window form)
{
_form=form;
}
void ExampleInUSerControl
{
_form.VerifyAccess();
_form.Activate();
_form.ShowActivated = true;
_form.ShowDialog();
}
The error message in this case is pretty accurate: once a Window is closed, it's closed for good. Since ShowDialog() always closes the window, you need to create a new instance of the window every time you call ShowDialog().
One fairly simple way to accomplish this in your example is to have the Load event take an argument of type Func<Window>:
In the MainWindow:
private Window MakeWindow()
{
return new MyWindow();
}
private void Example()
{
usercontrol.Load(MakeWindow);
}
In the user control:
public void Load(Func<T> makeWindow)
{
_form = makeWindow();
...
}
Note, by the way, that there should be no reason to call Activate or set ShowActivated - ShowDialog will do all that. And I don't know why you'd call VerifyAccess either.

Resources