WPF usercontrol showdialog problem - wpf

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.

Related

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?

How do I show windows sequentially in WPF?

I have the following code in App.xaml.cs:
protected override void OnStartup(StartupEventArgs e)
{
var window = new WelcomeWindow();
if (window.ShowDialog() == true)
{
var mainWindow = new MainWindow();
mainWindow.ShowDialog();
}
}
The second window never shows. Instead, the application simply closes when the Welcome window is closed. How do I ensure a second window can be shown after a first one is closed?
This is because default value of Application.ShutdownMode is OnLastWindowClose. This means when your WelcomeWindow is closed the application shuts down and you see nothing more.
To solve this set ShutdownMode to OnExplicitShutdown and call Shutdown explicitly if you want to exit your app.
public App()
{
this.ShutdownMode = ShutdownMode.OnExplicitShutdown;
}
What about to show WelcomeWindow on Initialized event of MainWindow and close last if Dialog is not true. This was you let MainWindow to stay the MainWindow of Application.
private void Window_Initialized(object sender, EventArgs e)
{
// at this moment MainWindow is Initialized but still nonvisible
if ((new WelcomeWindow()).ShowDialog()!=true)
{
this.Close();
}
}
When you load any window Application_Startup it become The MainWindow of application. And it will closed on this window closing.
I've checked that even if you have StartupUri="MainWindow.xaml" in you app.xaml it have no effect if some else window have been shown on Application StartUp event.
You may do it yourself. Just make breakpoint on your firstloaded window Loaded event handler and look in debuger on "Aplication.Current.MainWindow == this" expression result. It will be true.

Change WPF mainwindow label from another class and separate thread

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.
}
}

Silverlight MVVM moving the results from Child Window ViewModel to Parent Window ViewModel

Hi I have a silverlight MVVM application using MVVM light.
When I open the application a child window should popup and upon specifying the condition in the child window and clicking OK button the main window should display the details.
public MainPage()
{
ChildPage cp = new ChildPage();
cp.Show();
InitializeComponent();
}
upon hitting OK button on the child window this window should disappear and display a list of objects on the main window. In the View Model of the child window I have a RelayCommand OKCommand.
private void WireCommands()
{
OKCommand = new RelayCommand(GetEmployees);
}
private void GetEmployees()
{
IEnumerable<Employees> employees;
employees = from employee in Employees where employee.Name == selectedEmployee.Name select employee;
Employees= new ObservableCollection<Employee>(employees);
}
The Employees has the required result. But I dont know how to close the chils window and move the result to the parent window. Thanks in advance.
You can use (in increasing order of decoupling):
As you have a reference to the ChildPage in MainPage, you can access its properties.
Use standard .NET events, where the event is on the child page, and the subscribing is done in the MainPage
Use an event aggregator pattern. Several MVVM frameworks implement the event aggregator pattern.
Using .NET Events
ChildPage cp = new ChildPage();
cp.NameReceived += NameReceived;
cp.Show();
private void NameRecieved(object sender, NameReceivedEventArgs eventArgs)
{
// retrieve employees using eventargs.Name
}
Using Event Aggregator from Caliburn.Micro
public class MainPage : Screen, IHandle<NameReceivedMessage>
{
public MainPage(IEventAggregator eventAggregator)
{
eventAggregator.Subscribe(this);
}
public void Handle(NameReceivedMessage message)
{
// retrieve employees using message.Name which is the inputted name
}
}
Here we are doing the employee retrieval in the MainPage, after receiving the name from the ChildPage. Alternatively, you could retrieve the employees in the ChildPage, and pass them in the event args/message.
To close the child window, you can either use the Close() Method of the ChildWindow or you can set the DialogResult property to true or false which also close it.
You have to do it in the code-behind of ChildPage on the OnClick event of the OK Button.
To access the Employees property of the ChildPage's ViewModel you can do something like that :
public MainPage()
{
ChildPage cp = new ChildPage();
cp.Closed += (s,e) =>
{
//Do something with (cp.DataContext as ChildPageViewModel).Employees
}
cp.Show();
InitializeComponent();
}

WPF WindowStartupLocation="CenterOwner" not really center, and pops all over, why?

Well this question and this question are similar but no answers that work. In fact I was hoping WindowStartupLocation=CenterOwner would work...it doesn't. It seems to center the new window in the center of a grid column, not the center of the main window. So I'm assuming it thinks that is the parent. Second when I close the dialog and open it again it is not centered but moved down and right from the previous position. And if I move the main window to a second monitor the popup still opens on the default monitor. Are these properties wrong or am I just thinking it should work in a different way. I suppose I could calculate the Top and Left properties manually. I just want the popup to be centered in the main window no matter where it is.
Probably because you didn't set the owner:
this.Owner = App.MainWindow; // for example
That's how I do it and it centers the window perfectly all the time.
To extend on what Will Eddins commented, you could create an overload method for ShowDialog() or Show() in your Window:
public void ShowDialog(Window owner)
{
this.Owner = owner;
this.ShowDialog();
}
public void Show(Window owner)
{
this.Owner = owner;
this.Show();
}
Or overload a constructor:
public MyWindow(Window owner)
: this()
{
this.Owner = owner;
}
If you create an extention for this, you could reuse this fine idea:
/// <summary>
/// Opens a window modally, with an owner
/// </summary>
/// <param name="window">The window to open</param>
/// <param name="opener">The owner of the window getting opened</param>
/// <returns>window.ShowDialog()</returns>
public static bool? ShowDialog(this Window window, Window opener)
{
window.Owner = opener;
return window.ShowDialog();
}
In addition, we can use:
this.Owner = App.Current.MainWindow;
Or Application instead of App.
And place it in a child window constructor:
public partial class ChildWindow : Window
{
public ChildWindow()
{
InitializeComponent();
DataContext = new ChildWindowViewModel();
this.Owner = App.Current.MainWindow;
}
}
I had the same problem...but it was mostly due to the fact that, when i wanted to get rid of the child window, I used hide() instead of close() ... so when you reopen it, because it was hidden and not closed, when the parent window is moved, it still opens at it's startup location...
So when close the child window instead of hiding it for example when finished working with it.
Something else that can cause this is setting DataContext after InitializeComponent() is called.
If you have code-behind like this:
public CustomWindow(CustomViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
Change it to:
public CustomWindow(CustomViewModel viewModel)
{
DataContext = viewModel;
InitializeComponent();
}

Resources