I am currently working on an app that uses the tool "TimePicker" in Blend for Visual Studio 2017 and I have a question regarding an event I'm trying to find. After a time is selected I wish to have separate events to occur when check-button is pressed that changes the time and if the X-button is pressed to cancel and return from the flyout. I do believe I can use the TimePicker's TimeChanged event to tell whether or not the check button was pressed but I cannot figure out which event would work for if the x-button to cancel was pressed. Does anyone know?
There's no obvious way to do it. There's a Dismiss button in the TimePickerFlyoutPresenter, but getting access to that is not enough. You'll also need to manage keyboard shortcuts, like the [ESC] key.
You got me a bit curious on how to hack it, so here's what I've come up with so far. Here's a TimePickerDismissal class I hacked-up real quick. The idea is if the TimePicker gains focus after it pushed a popup and it did not report a time change, then it's considered a dismiss.
public class TimePickerDismissal
{
private bool _active;
private bool _timeChanged;
public event EventHandler Dismissed;
public TimePickerDismissal(TimePicker timer)
{
timer.GotFocus += OnTimeGotFocus;
timer.LostFocus += OnTimeLostFocus;
timer.TimeChanged += OnTimeChanged;
}
private void OnTimeGotFocus(object sender, RoutedEventArgs e)
{
if (!_active)
{
return;
}
_active = false;
if (!_timeChanged)
{
Dismissed?.Invoke(this, EventArgs.Empty);
}
_timeChanged = false;
}
private void OnTimeLostFocus(object sender, RoutedEventArgs e)
{
var selector = FocusManager.GetFocusedElement() as LoopingSelector;
if (selector == null)
{
return;
}
_active = true;
}
private void OnTimeChanged(object sender, TimePickerValueChangedEventArgs e)
{
_timeChanged = true;
}
}
Here's how to use it:
public sealed partial class MainPage
{
public MainPage()
{
InitializeComponent();
var dismissal = new TimePickerDismissal(MyTimePicker);
dismissal.Dismissed += OnTimerDismissed;
}
private void OnTimerDismissed(object sender, EventArgs e)
{
Debug.WriteLine("TimePicker dismissed!");
}
}
Give that a shot. Let me know if it works for you. You can probably turn this into a behavior actually...
Related
Can someone please illustrate for me how to set up a logic like this:
I have a WPF Control. When a button is pressed it does one of the two possible things.
A. It checks if a different WPF Window has been loaded. If it was, it triggers that window's Print method.
B. It checks if a different WPF Window has been loaded. If it was not, it instantiates that window and then triggers its Print method.
I struggle to understand the events system between two WPF Controls/Windows. It's a relatively new thing for me, so I would appreciate if someone walked me through this.
Ps. This is not a homework assignment, but rather a new hobby of mine. If its a totally noob question then just point me to a resource so I can educate myself.
Cheers!
First of all, what is the way by which you will check if new Window opened is what you need it to be ?
You might do this by comparing their Handle or their Type (public class MyWindowWithPrintMethod : Window).
There can be multiple ways of doing this.
I suggest my simple way, focusing on the WPF way, to solve your purpose in easiest way possible.
MyWindowWithPrintMethod obj_MyWindowWithPrintMethod;
private void btnNewWindow_Click(object sender, RoutedEventArgs e)
{
obj_MyWindowWithPrintMethod = new MyWindowWithPrintMethod();
obj_MyWindowWithPrintMethod.Show();
}
private void btnCheckNewWindow_Click(object sender, RoutedEventArgs e)
{
WindowInteropHelper tgtWindow = new WindowInteropHelper(obj_MyWindowWithPrintMethod);
foreach (Window w in Application.Current.Windows)
{
// Compare Handle
WindowInteropHelper wih = new WindowInteropHelper(w);
if (wih.Handle == tgtWindow.Handle)
{
((MyWindowWithPrintMethod)w).Print();
}
// Compare Type
if (w.GetType() == typeof(MyWindowWithPrintMethod))
{
((MyWindowWithPrintMethod)w).Print();
}
}
}
MyWindowWithPrintMethod.cs
public class MyWindowWithPrintMethod : Window
{
public void Print()
{
MessageBox.Show("Print invoked !");
}
}
This answer from this question about events from 2 windows may help:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Child childWindow = new Child();
childWindow.MyEvent += new EventHandler(childWindow_MyEvent);
childWindow.ShowDialog();
}
void childWindow_MyEvent(object sender, EventArgs e)
{
// handle event
MessageBox.Show("Handle");
}
}
Child window
public partial class Child : Window
{
// define event
public event EventHandler MyEvent;
protected void OnMyEvent()
{
if (this.MyEvent != null)
this.MyEvent(this, EventArgs.Empty);
}
public Child()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Child_Loaded);
}
void Child_Loaded(object sender, RoutedEventArgs e)
{
// call event
this.OnMyEvent();
}
}
The above code shows how to set up an event from one window to another. But, you might want to simply call a method in that other window instead. For example:
public void AddNewUser()
{
Window2 window = new Window2();
if (window.ShowDialog() == true)
{
// Update DataGrid
RefreshDataGrid();
}
}
If you are determined to stick with events, then you should read up on WPF routed events.
I'm fairly new to programming so sorry if this is simple, but I might just be missing the obvious! I have the following form which is automatically populated on load from settings stored in an INI file:
The whole form is working just as I want it to apart form one small part. The 'Close' button currently just closes the form so that if any values have changed in the text boxes since the form was loaded, the changes are lost. I want to instead prompt the user to use the Save button instead otherwise the changes will be lost.
I've been trying to do something along these lines on my close button where the value of the text boxes are checked against the variable values that they were originally populated with:
private void btnClose_Click(object sender, EventArgs e)
{
if (txtName.Text != name || txtSchName.Text != schname || txtServer1.Text != svr1 etc etc etc)
{
Warn User changes will be lost, ask user if really OK to close?
if (User chooses to to close)
{
this.Close();
}
else
{
Go back to the config form;
}
}
else
{
this.Close();
}
With over 21 text fields, I was not sure if this was the most "tidy way" of checking for changes? Any pointers would be appreciated.
You just add a global boolean variable and write an handler for the TextChanged event
// Declared at the form level
private bool _modified = false;
public Form1()
{
InitializeComponent();
// This could be done in the form designer of course
// o repeated here for every textbox involved....
txtName.TextChanged += OnBoxesChanged;
......
}
private void Form1_Load(object sender, EventArgs e)
{
.....
// Code that initializes the textboxes could raise the TextChanged event
// So it is better to reset the global variable to an untouched state
_modified = false;
}
private void OnBoxesChanged(object sender, EventArgs e)
{
// Every textbox will call this event handler
_modified = true;
}
private void btnClose_Click(object sender, EventArgs e)
{
if(_modified)
{
// Save, warning, whatever in case of changes ....
}
}
Just set the event handler OnBoxesChanged for every textbox you want to trigger the condition. You could do it through the Designer or manually after the InitializeComponent call
What you are looking for is Dirty Tracking. Dirty tracking is used to track states of your control. Here is a simple reusable approach to track your controls
public class SimpleDirtyTracker
{
private Form _frmTracked;
private bool _isDirty;
public SimpleDirtyTracker(Form frm)
{
_frmTracked = frm;
AssignHandlersForControlCollection(frm.Controls);
}
// property denoting whether the tracked form is clean or dirty
public bool IsDirty
{
get { return _isDirty; }
set { _isDirty = value; }
}
// methods to make dirty or clean
public void SetAsDirty()
{
_isDirty = true;
}
public void SetAsClean()
{
_isDirty = false;
}
private void SimpleDirtyTracker_TextChanged(object sender, EventArgs e)
{
_isDirty = true;
}
private void SimpleDirtyTracker_CheckedChanged(object sender, EventArgs e)
{
_isDirty = true;
}
// recursive routine to inspect each control and assign handlers accordingly
private void AssignHandlersForControlCollection(
Control.ControlCollection coll)
{
foreach (Control c in coll)
{
if (c is TextBox)
(c as TextBox).TextChanged
+= new EventHandler(SimpleDirtyTracker_TextChanged);
if (c is CheckBox)
(c as CheckBox).CheckedChanged
+= new EventHandler(SimpleDirtyTracker_CheckedChanged);
// ... apply for other desired input types similarly ...
// recurively apply to inner collections
if (c.HasChildren)
AssignHandlersForControlCollection(c.Controls);
}
}
and in your mainform
public partial class Form1 : Form
{
private SimpleDirtyTracker _dirtyTracker;
private void Form1_Load(object sender, EventArgs e)
{
_dirtyTracker = new SimpleDirtyTracker(this);
_dirtyTracker.SetAsClean();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
// upon closing, check if the form is dirty; if so, prompt
// to save changes
if (_dirtyTracker.IsDirty)
{
DialogResult result
= (MessageBox.Show(
"Would you like to save changes before closing?"
, "Save Changes"
, MessageBoxButtons.YesNoCancel
, MessageBoxIcon.Question));
}
}
If you want to save for example usersettings you could create Settings in your Properties.
You can get them like this:
string anyProperty = WindowsFormsApplication1.Properties.Settings.Default.TestSetting;
You save them using the Save-method:
WindowsFormsApplication1.Properties.Settings.Default.TestSetting = "Hello World";
WindowsFormsApplication1.Properties.Settings.Default.Save();
You can call the Save-method after you have clicked the close-button.
For saving several string properties you could create a property of the type System.Collections.Specialized.StringCollection, so that you just create one property and not 21.
One disadvantage of dirtytracer is, that it returns "dirty" even when changes revert to original state. If You use object in DataContext as Your data model, all this task simplifies to:
bool changed = dataContext.SomeTables.GetModifiedMembers(someRow).Any();
I'd like the activated event to only run once. I've tried using an If condition but the Reload variable doesn't set to false and thus it keeps looping endlessly. Is there a way around this?
Form1.cs code:
private void Form1_Activated(object sender, EventArgs e)
{
if (Class1.Reload == true) {
Class1.Reload = false;
}
}
Class1.cs code:
public class Class1 {
public static void Refresh() { Reload = true; }
public static bool Reload { get; set; }
Just unsubscribe from the event the first time it is triggered.
private void Form1_Activated(object sender, EventArgs e)
{
this.Activated -= Form1_Activated;
// Do other stuff here.
}
While CathalMF's solution is valid, I'll post the solution I implemented, whose aim was to refresh a DatagridView when I come back to the main form.
private void Form1_Activated(object sender, EventArgs e) {
if (Class1.Reload == true) {
Activated -= Form1_Activated;
Class1.Reload = false;
//Here I implement the code to refresh a DatagridView
Activated += Form1_Activated;
}
}
Class1.cs stays the same.
I have a form with 2 tabs on it. I can chose the tab viewed after initialization and I need some initial code every time after the tab2 is initialized:
public partial class SetupComponent : Form
{
public SetupComponent(bool tab2)
{
InitializeComponent();
if (tab2)
{
this.tabControl1.SelectedTab = tabPage2;
}
}
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
textBox2.SelectionStart = textBox2.Text.Length;
textBox2.Focus();
}
}
if I call this class with tab2=false and then click onto tab2, tabControl1_SelectedIndexChanged is called.
But if I select the tab2=true during SetupComponent, I find no possibility to do that code. All the TabControl1_Events I found are too early and I don`t find a matching TabPage2_Event.
How can I manage it?
I managed this problem using the Paint_Event:
bool activated = false;
private void tabPage2_Paint(object sender, PaintEventArgs e)
{
if (!activated)
{
tabControl1_SelectedIndexChanged(null, null);
activated = true;
}
}
I use the variable because the Paint_Event is called many times.
In WPF you get to call ShowDialog on a window exactly once. After that it is done for.
Seems kind of lame to me, but those are the rules. If you call ShowDialog again you get this exception:
Cannot set Visibility or call Show, ShowDialog, or WindowInteropHelper.EnsureHandle after a Window has closed
What I want to know is: How can I take a Window (or UserControl really) and check to see if it has had ShowDialog called (so I know to new up a different one before calling ShowDialog again).
Something like this:
public void ShowListOfClients()
{
// | This is the method I want to write
// V
RefreshViewIfNeeded(_myWindowOrUserControlThatShowsAList);
FillWindowWithBusinessData(_myWindowOrUserControlThatShowsAList);
_myWindowOrUserControlThatShowsAList.ShowDialog();
}
NOTE: Clearly in the above example it would be easier to just create a new WindowOrUserControlThatShowsAList every time I enter the method. But please consider the question more that the dumbed down example.
This isn't exclusive to ShowDialog(), Show() does it too. And no, there is no IsDisposed property to check. IsLoaded is only half a solution, it will be false for the 1st invocation as well.
First approach is to just make a dialog that can be re-shown:
public bool CloseAllowed { get; set; }
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
if (!CloseAllowed) {
this.Visibility = System.Windows.Visibility.Hidden;
e.Cancel = true;
}
}
The next one is to explicitly keep track of the health of the object reference:
private Window1 win = new Window1(); // say
private void button1_Click(object sender, RoutedEventArgs e) {
if (win == null) {
win = new Window1();
win.Closing += delegate { win = null; };
}
win.ShowDialog();
}
Well the dirty way to do it would be to catch the exception.
The clean way to do it would be to show a window with ShowDialog, and destroy (lose reference to, etc) the window when the function returns. The view should not be tightly coupled with the models (you are using MVVM right?) so creating new visual objects for each client view should not be an issue.
Easy way to deal with this problem without messing up with the Closing event :
public partial class MainWindow
{
private SomeCustomWindow _someCustomWindow;
public MainWindow()
{
InitializeComponent();
}
private void OnOpenCustomWindowButtonClick(object sender, RoutedEventArgs e)
{
if (_someCustomWindow != null)
_someCustomWindow.Close();
_someCustomWindow = new SomeCustomWindow();
_someCustomWindow.ShowDialog();
}
private void OnWindowClosing(object sender, CancelEventArgs e)
{
if (_someCustomWindow!= null)
_someCustomWindow.Close();
}
}