I have a program when I log in, I have to wait for loading database from server. So I created a "Loading..." window. After loading the database, this will automatically close the "Loading" window, the "Login" window and open the MainWindow.xaml.
But myprogram have a problem: when I use waitForm.show() in Login.xaml.cs, it runs good, but the controls on "Waiting" window like progressbar, textblock, it doesn't display.If I use waitForm.showdialog(), it will display progressbar and textblock control. But it won't automatically close. So my Mainwindow.xaml not open.
Is it possible to use show() but show the controls?
Sorry about my bad English. Thank you.
Waiting.xaml
<Window x:Class="TMO.Waiting"
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:TMO"
mc:Ignorable="d"
Title="Waiting" Height="100" Width="300" WindowStartupLocation="CenterScreen" WindowStyle="None">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ProgressBar Margin="10,20,10,5" Minimum="0" Maximum="100" Name="pbStatus" IsIndeterminate="True" Grid.Row="0"/>
<TextBlock Grid.Row="1" Margin="0,5,0,5" TextAlignment="Center" VerticalAlignment="Center" FontSize="20" FontWeight="Bold" >Loading...</TextBlock>
</Grid>
</Window>
Waiting.xaml.cs
using System.Windows;
namespace TMO
{
/// <summary>
/// Interaction logic for Waiting.xaml
/// </summary>
public partial class Waiting : Window
{
public static MessageBoxResult result;
public Waiting()
{
InitializeComponent();
}
public Waiting(Window parent1)
{
InitializeComponent();
}
public void CloseWaiting()
{
DialogResult = true;
this.Close();
}
}
}
WaitFunc.cs
using System.Threading;
using System.Windows;
namespace TMO
{
public class WaitFunc
{
Waiting wait;
Thread loadthread;
public void show()
{
loadthread = new Thread(new ThreadStart(LoadingProcess));
loadthread.Start();
}
public void show(Window parent)
{
loadthread=new Thread(new ParameterizedThreadStart(LoadingProcess));
loadthread.Start(parent);
}
public void Close()
{
if(wait!=null)
{
wait.Dispatcher.BeginInvoke(new System.Threading.ThreadStart(wait.CloseWaiting));
wait=null;
loadthread=null;
}
}
public void LoadingProcess()
{
wait=new Waiting();
wait.ShowDialog();
}
public void LoadingProcess(object parent)
{
Window parent1 = parent as Window;
wait=new Waiting(parent1);
wait.ShowDialog();
}
}
}
Login.xaml.cs
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
this.Hide();
Waiting waitForm = new Waiting();
waitForm.Show(); //If I use waitForm.showdialog() it will display all control
Thread.Sleep(500);
MainWindow main = new MainWindow(txtUserName.Text);
this.Hide();
waitForm.Close();
main.Show();
this.Close();
}
You should manage all windows of the application launch sequence from the entry point of your application. Therefore, I recommend to move the logic to start the MainWindow from the Login.xaml.cs to the App.xaml.cs.
Furthermore, to create a UI thread that can show controls and allows input handling you must explicitly configure the thread to be a STA thread and additionally start the Dispatcher message loop.
Note, if you use async APIs and background threads when necessary (use Task.Run and not Thread), then you don't need an extra UI thread only to show the splash screen.
You should add a LoginSuccessful and a LoginFailed event to the LoginWindow.
Your flow could be implemented like this:
App.xaml
<Application Startup="App_OnStartup" />
App.xaml.cs
public partial class App : Application
{
private Window SplashScreen { get; set; }
private void App_OnStartup(object sender, StartupEventArgs e)
{
ShutdownMode = ShutdownMode.OnExplicitShutdown;
var loginScreen = new LoginWindow();
loginScreen.LoginSuccessful += RunApplication_OnLoginSuccessful;
loginScreen.LoginFailed += ShutdownApplication_OnLoginFailed;
loginScreen.Show();
}
private void RunApplication_OnLoginSuccessful(object sender, EventArgs e)
{
var newUiThread = new Thread(new ThreadStart(ShowSplashScreen))
{
ApartmentState = ApartmentState.STA,
IsBackground = false,
};
newUiThread.Start();
var mainWindow = new MainWindow();
mainWindow.Loaded += CloseSplashScreen_OnMainWindowLoaded;
mainWindow.Closed += ShutdownApplication_OnMainWindowClosed;
// TODO::Initialize application
mainWindow.Show();
}
private void ShowSplashScreen()
{
this.SplashScreen = new Window() { Content = "SplashScreen" };
this.SplashScreen.Show();
// Start event queue
Dispatcher.Run();
}
private void CloseSplashScreen_OnMainWindowLoaded(object sender, RoutedEventArgs e)
{
this.SplashScreen.Dispatcher.InvokeAsync(() =>
{
this.SplashScreen.Close();
this.SplashScreen.Dispatcher.InvokeShutdown();
});
}
private void ShutdownApplication_OnMainWindowClosed(object sender, EventArgs e) => Shutdown();
private void ShutdownApplication_OnLoginFailed(object sender, EventArgs e) => Shutdown();
Related
once to explain, I open a xaml page via "frame.content" and in this page I have opened, I want to open another one but on the frame where the second page is running.
but i can't open the page,
nothing happens. not even an expection.
So here what I have written:
This is the class from the page that is open
private void bttn_start(object sender, RoutedEventArgs e)
{
MainWindow mw = new MainWindow();
mw.JoinNextPage();
}
This is the MainWindow class where the frame is.
public partial class MainWindow : Window
{
public void JoinNextPage() => pageMirror.Content = new page_finish();
}
You should use RoutedCommand to trigger the Frame navigation instead of the static MainWindow reference.
This removes the complete navigation logic (button event handlers) from your pages.
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public static RoutedCommand NextPageCommand { get; } = new RoutedCommand("NextPageCommand", typeof(MainWindow));
public MainWindow()
{
InitializeComponent();
CommandBindings.Add(
new CommandBinding(NextPageCommand, ExecuteNextPageCommand, CanExecuteNextPageCommand));
}
private void CanExecuteNextPageCommand(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void ExecuteNextPageCommand(object sender, ExecutedRoutedEventArgs e)
{
// Logic to select the next Frame content
JoinNextPage();
}
}
MainWindow.xaml
<Window>
<Frame>
<Frame.Content>
<Page>
<Button Command="{x:Static local:MainWindow.NextPageCommand}"
Content="Next Page" />
</Page>
</Frame.Content>
</Frame>
</Window>
Try this:
private void bttn_start(object sender, RoutedEventArgs e)
{
MainWindow mw = (MainWindow)Application.Current.MainWindow;
mw.JoinNextPage();
}
Instead of using a traditional splash screen, I want to display a login window to allow the user to enter their credentials. Meanwhile, in the background, I want to initialize the app, then load the main window. The problem is that the login window is covered up by the main window once shown.
private void App_OnStartup(object sender, StartupEventArgs e)
{
Current.MainWindow = new LoginWindow();
Current.MainWindow.Show();
Task.Run(() => { /*do app startup background stuff*/ }).ContinueWith(t =>
{
var appWindow = new ApplicationWindow();
appWindow.Show();
Current.MainWindow.Owner = appWindow;
}, TaskScheduler.FromCurrentSynchronizationContext());
The login window is made the main window from the start. My assumption was that by setting the ApplicationWindow's owner to the login window, the login window would remain on top. If I'm doing it wrong, is there a way to achieve what I want? The topmost flag works but then the window is, well, topmost, which is undesirable.
Suppose you have a MainWindow:
<Window x:Class="SO40189046.MainWindow"
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:SO40189046"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBlock Name="TimeText" />
</Grid>
</Window>
with code behind:
using System;
using System.Threading;
using System.Windows;
namespace SO40189046
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
// Background thread initializing the MainWindow
ThreadPool.QueueUserWorkItem((state) =>
{
for (int i = 0; i < 10; i++)
{
Dispatcher.Invoke(() =>
{
TimeText.Text = DateTime.Now.ToString();
});
Thread.Sleep(1000);
}
});
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
LoginWindow login = new LoginWindow();
login.Owner = App.Current.MainWindow;
login.WindowStartupLocation = WindowStartupLocation.CenterOwner;
if (!login.ShowDialog().GetValueOrDefault())
{
Close();
}
}
}
}
Then you can initialize your MainWindow while showing the login dialog.
AND you load the MainWindow as normal via StartUpUri in App.xaml
I have my xaml:
<Window x:Class="Views.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Height="768" Width="1024" WindowStartupLocation="CenterScreen"
Title="{Binding Path=DisplayName}">
<xctk:BusyIndicator x:Name="BusyIndicator" IsBusy="{Binding IsBusy, UpdateSourceTrigger=PropertyChanged}" >
<TreeView Style="{StaticResource TableSchemaTreeViewStyle}" ItemContainerStyle="{StaticResource SchemaTreeViewStyle}" Margin="0,15,0,0"
x:Name="TreeViewSchema"
TreeViewItem.Expanded="TreeViewSchema_OnExpanded"
TreeViewItem.Selected="TreeViewSchema_OnSelected"
Grid.Row="2"
ItemsSource="{Binding CurrentProject.Tables, Converter={StaticResource TreeViewSortingConverter}, ConverterParameter=FullTableName}">
</TreeView>
</xctk:BusyIndicator>
</Window>
And suppose I have long running task in code-behind which is performed on UI thread (long filtering of treeview, it can have more then 1000 tables and each table more than 100 columns in it).
Lets say I iterate and set tableTreeViewItem.Visibility = Visibility.Collapsed; for each item.
What I want: show BusyIndicator by setting it to true before this action:
BusyIndicator.IsBusy = true;.
The problem: both actions on UI thread and binding does not work as expected. I tried few things:
BusyIndicator.IsBusy = true;
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
tableTreeViewItem.Visibility = Visibility.Collapsed;
}, CancellationToken.None, TaskCreationOptions.None, uiScheduler).ContinueWith(task => Dispatcher.Invoke(() =>
{
BusyIndicator.IsBusy = false;
}));
And using dispatcher:
BusyIndicator.IsBusy = true;
//long-running UI task
tableTreeViewItem.Visibility = Visibility.Collapsed;
BusyIndicator.IsBusy = false;
But it does not work, any ideas how to fix it?
PSS
I made some updates, I decided to grab all data and store which tree view item should be visible or hidden.
So I have class which stores table, visibility and visible columns for filter method
class TreeViewItemVisibilityTableContainer
{
private TreeViewItem _treeViewItem;
private TableModel _table;
private Visibility _visibility;
private List<ColumnModel> _visibleColumns;
public TableModel Table
{
get { return _table; }
set { _table = value; }
}
public TreeViewItem TreeViewItem
{
get { return _treeViewItem; }
set { _treeViewItem = value; }
}
public Visibility Visibility
{
get { return _visibility; }
set { _visibility = value; }
}
public List<ColumnModel> VisibleColumns
{
get { return _visibleColumns; }
set { _visibleColumns = value; }
}
}
And now I can filter all this staff directly on UI thread:
System.Action filterTreeViewItemsVisibility = () => Dispatcher.Invoke(() =>
{
foreach (var item in itemsToFilter)
{
item.TreeViewItem.Visibility = item.Visibility;
var capturedItemForClosure = item;
if (item.Visibility == Visibility.Visible)
{
if (item.VisibleColumns.Any())
{
item.TreeViewItem.Items.Filter = item.TreeViewItem.Items.Filter =
treeViewItem =>
capturedItemForClosure.VisibleColumns.Any(
columnModel => columnModel.Equals(treeViewItem));
}
else
{
item.TreeViewItem.Visibility = Visibility.Collapsed;
}
}
}
});
IoC.Get<IBusyIndicatorHelper>().PerformLongrunningAction(filterTreeViewItemsVisibility, IoC.Get<IShellViewModel>());
But it is still super slow
Here is my solution for the busy indicator.
There are few components to show the solutions
BusyIndicator User Control
AbortableBackgroundWorker
MainWindow as application window
BusyIndicator user control
BusyIndicator.xaml - pretty simply
<UserControl x:Class="BusyIndicatorExample.BusyInidicator"
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" Visibility="Collapsed">
<Grid Background="#BFFFFFFF" >
<TextBlock Text="Loading data..." HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="#FF2C2C2C" FontSize="16" FontWeight="Bold" />
</Grid>
</UserControl>
BusyIndicator.xaml.cs
using System;
using System.Windows.Controls;
namespace BusyIndicatorExample
{
/// <summary>
/// Interaction logic for BusyInidcator.xaml
/// </summary>
public partial class BusyInidicator : UserControl
{
public BusyInidicator()
{
InitializeComponent();
}
Method for showing indicator
public void Start()
{
this.Dispatcher.Invoke(new Action(delegate()
{
this.Visibility = System.Windows.Visibility.Visible;
}), System.Windows.Threading.DispatcherPriority.Normal);
}
Method for hiding indicator
public void Stop()
{
this.Dispatcher.Invoke(new Action(delegate()
{
this.Visibility = System.Windows.Visibility.Collapsed;
}), System.Windows.Threading.DispatcherPriority.Normal);
}
}
}
AbortableBackgroundWorker for simulation non UI task
using System;
using System.ComponentModel;
using System.Threading;
namespace BusyIndicatorExample
{
/// <summary>
/// Abortable background worker
/// </summary>
public class AbortableBackgroundWorker : BackgroundWorker
{
//Internal Thread
private Thread workerThread;
protected override void OnDoWork(DoWorkEventArgs e)
{
try
{
base.OnDoWork(e);
}
catch (ThreadAbortException)
{
e.Cancel = true; //We must set Cancel property to true!
Thread.ResetAbort(); //Prevents ThreadAbortException propagation
}
}
public void Abort()
{
if (workerThread != null)
{
workerThread.Abort();
workerThread = null;
}
}
}
}
Finally, MainWindow where the process is simulated
MainWindow.xaml
<Window x:Class="BusyIndicatorExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BusyIndicatorExample"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Start Data Loading" HorizontalAlignment="Left" Margin="63,42,0,0" VerticalAlignment="Top" Width="125" Height="28" Click="Button_Click"/>
<TextBox HorizontalAlignment="Left" Height="23" Margin="63,87,0,0" TextWrapping="Wrap" Text="{Binding DataString}" VerticalAlignment="Top" Width="412"/>
<local:BusyInidicator x:Name="busyIndicator" HorizontalAlignment="Left" Height="100" Margin="177,140,0,0" VerticalAlignment="Top" Width="300"/>
</Grid>
</Window>
MainWindow.xaml.cs - here is the application code
using System.ComponentModel;
using System.Windows;
namespace BusyIndicatorExample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
private AbortableBackgroundWorker _worker;
Constructor and public property binded to the textbox
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private string _dataString = "No Data";
public string DataString
{
get { return _dataString; }
set {
if (_dataString != value)
{
_dataString = value;
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("DataString"));
}
}
}
Button click event - initialize BackgroundWorker and starts it
private void Button_Click(object sender, RoutedEventArgs e)
{
if(_worker == null)
{
_worker = new AbortableBackgroundWorker();
_worker.WorkerReportsProgress = true;
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += _worker_DoWork;
_worker.RunWorkerCompleted += _worker_RunWorkerCompleted;
}
if (!_worker.IsBusy)
_worker.RunWorkerAsync();
}
BackgroundWorker event handlers
RunWorkerCompleted update data string and hide indicator.
void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
DataString = "Data has been loaded";
busyIndicator.Stop();
}
DoWork shows indicator and put to sleep thread for 5 sec.
void _worker_DoWork(object sender, DoWorkEventArgs e)
{
DataString = "No Data";
busyIndicator.Start();
System.Threading.Thread.Sleep(5000);
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Hope this helps. Modify code as you need to fit to your scenario
Full example project code can be downloaded here
I implemented the control basics sample from the Kinect for Windows toolkit http://msdn.microsoft.com/en-us/library/dn188701.aspx to control the cursor with a users hand, but when I click on a sub window and then re open the main window with the hand cursor doesn't show.
My question is how do I open a new window without closing the previous window and then navigate back to the same instance of that window, not a new instance?
This is how I call a new window in my main window class:
private void trainingBtn_Click(object sender, RoutedEventArgs e)
{
var newForm = new TrainingFrm(); //create your new form.
newForm.Show(); //show the new form.
this.Close(); //only if you want to close the current form.
}
And this is how I reopen the main window, but it creates anew instance of the main window which I don't want.
private void homeBtn_Click(object sender, RoutedEventArgs e)
{
var newForm = new MainWindow(); //create your new form.
newForm.Show(); //show the new form.
this.Close(); //only if you want to close the current form.
}
What you need is composition
Here how it should look your mainWindow class
public partial class MainWindow : Window
{
private trainingWindow _trainingWindow;
public MainWindow()
{
InitializeComponent();
}
private void buttonGoTraining_Click(object sender, RoutedEventArgs e)
{
if (_trainingWindow== null)
{
_trainingWindow= new trainingWindow(this);
}
this.Visibility = Visibility.Hidden;
_trainingWindow.Show();
_trainingWindow.Visibility = Visibility.Visible;
this.Visibility = Visibility.Hidden;
}
}
and here is your training class
public partial class trainingWindow : Window
{
private MainWindow _mainWindow;
public trainingWindow(MainWindow mainWindow )
{
InitializeComponent();
_mainWindow = mainWindow;
}
private void biuttonBack_Click(object sender, RoutedEventArgs e)
{
this.Visibility = Visibility.Hidden;
_mainWindow.Visibility = Visibility.Visible;
}
}
here is the xaml
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Button" Height="121" HorizontalAlignment="Left" Margin="112,38,0,0" Name="button1" VerticalAlignment="Top" Width="195" Click="buttonGoTraining_Click" />
</Grid>
</Window>
<Window x:Class="WpfApplication2.trainingWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="trainingWindow" Height="300" Width="300">
<Grid>
<Button Content="Button" Height="36" HorizontalAlignment="Left" Margin="52,33,0,0" Name="button1" VerticalAlignment="Top" Width="97" Click="biuttonBack_Click" />
</Grid>
</Window>
Simply hide it, and not close.
If you need to show a fresh information after show, just bind a new data to its view model.
In a WPF application I would like to implement the following behaviour which doesn't seem to work straightforward:
From the main window (Window1) the user opens a non-modal window (Window2), and that non-modal window may display a modal dialog (Window3).
The problem is that whenever the modal dialog has been shown, the main window disappears in the background (given that there are windows of other applications open) when the user closes the dialogs.
Is there anything wrong in the way that I use Window.Owner and Window.Show()/Window.ShowDialog(), is it a bug or is it something simply not supported?
The following simple WPF application demonstrates this behavior:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Window2 win = new Window2();
win.Owner = this;
win.Show();
}
}
public partial class Window2 : Window
{
public Window2()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Window3 win = new Window3();
win.Owner = this;
win.ShowDialog();
}
private void btnClose_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
public partial class Window3 : Window
{
public Window3()
{
InitializeComponent();
}
private void btnClose_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
XAML Window1:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1">
<Button Click="Button_Click">Show non-modal window</Button>
</Window>
XAML Window2:
<Window x:Class="WpfApplication1.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window2">
<StackPanel>
<Button Click="Button_Click">Show modal dialog</Button>
<Button Name="btnClose" Click="btnClose_Click">Close</Button>
</StackPanel>
</Window>
XAML Window3:
<Window x:Class="WpfApplication1.Window3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window3">
<Button Name="btnClose" Click="btnClose_Click">Close</Button>
</Window>
UPDATE: Fixed copy&paste error in the code. This is .NET 3.5 SP1 in case it matters.
Microsoft confirms this as a bug in WPF:
This isn't a regression from previous releases so it doesn't make the bar to be fixed for this version of the product. We'll look into this for a future release.
In the meantime, this can be worked around by activating the owner window when the child window is closing.
Sample code:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void NonModalButtonClick(object sender, RoutedEventArgs e)
{
new Window1 { Owner = this }.Show();
}
private void ModalButtonClick(object sender, RoutedEventArgs e)
{
new Window1 { Owner = this }.ShowDialog();
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
if (this.Owner != null)
{
this.Owner.Activate();
}
}
}
(Note that the workaround will always bring the main window into foreground which might be different than the expected behavior)