I am using a UserControl with a UITypeEditor. The user control has OK and Cancel buttons that do nothing except display a MessageBox with either OK or Cancel and then hide the user control. But when I click one of the buttons, the PropertyGrid displays an empty box where the UserControl was until I click away. Then the box disappears and the dialog is displayed.
Here is the user control code:
using System;
using System.Windows.Forms;
namespace j2associates.Tools.Winforms.Controls.DesignTimeSupport.SupportingClasses
{
public partial class SimpleTest : UserControl
{
public bool Cancelled { get; set; }
public SimpleTest()
{
InitializeComponent();
}
private void btnOK_Click(object sender, EventArgs e)
{
Cancelled = false;
this.Hide();
}
private void btnCancel_Click(object sender, EventArgs e)
{
Cancelled = true;
this.Hide();
}
}
}
Here is the UITypeEditor code:
using System;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using j2associates.Tools.Winforms.Controls.DesignTimeSupport.SupportingClasses;
namespace j2associates.Tools.Winforms.Controls.DesignTimeSupport.Editors
{
internal class TimeElementsEditor : UITypeEditor // PropertyEditorBase<TimeElementsUserControl>
{
public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.DropDown;
}
public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value)
{
if (value.GetType() == typeof(j2aTimePicker.TimeElementOptions))
{
var editorService = provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
if (editorService != null)
{
using (var st = new SimpleTest())
{
editorService.DropDownControl(st);
if (st.Cancelled)
{
MessageBox.Show("Cancel");
}
else
{
MessageBox.Show("OK");
}
editorService.CloseDropDown();
}
}
}
return value;
}
}
}
Any ideas and/or suggestions would be appreciated.
Sigh, it's always easy when you find it.
I needed to pass the IWindowsFormsEditorService in via an overloaded constructor, cache it and then call it's CloseDropdown method in the Button Click events instead of hiding the user control. It now works as expected.
/// <summary>
/// Displays an OK and Cancel button. When one is pressed,
/// the dialog is closed and a message box is displayed.
/// The actual value of the property is unchanged throughout.
/// </summary>
/// <remarks>The ToolboxItem attribute prevents the control from being displayed in the ToolKit.</remarks>
[ToolboxItem(false)]
public partial class SimpleTest : UserControl
{
public bool Cancelled { get; set; }
private IWindowsFormsEditorService m_EditorService;
// Require the use of the desired overloaded constructor.
private SimpleTest()
{
InitializeComponent();
}
internal SimpleTest(IWindowsFormsEditorService editorService)
: this()
{
// Cache the editor service.
m_EditorService = editorService;
}
private void btnOK_Click(object sender, EventArgs e)
{
Cancelled = false;
m_EditorService.CloseDropDown();
}
private void btnCancel_Click(object sender, EventArgs e)
{
Cancelled = true;
m_EditorService.CloseDropDown();
}
}
And here is the modified editor call:
using (var simpleTest = new SimpleTest(editorService))
{
editorService.DropDownControl(simpleTest);
MessageBox.Show(simpleTest.Cancelled ? "Cancelled" : "OK");
}
Related
I am trying to use a new IDialogService which was discussed in github issue 1666. A New IDialogService for WPF. I like this new feature but I can't find a solution for one case of using IDialogService in compare with InteractionRequest.
There is a button, pressing on which non-modal dialog is opened. If user press the same button one more time, while dialog still open, dialog close. How this behavior should be implemented in a proper way?
MainWindowViewModel
public class MainWindowViewModel : BindableBase
{
private readonly IDialogService _dialogService;
public DelegateCommand CustomPopupCommand { get; }
public MainWindowViewModel(IDialogService dialogService)
{
_dialogService = dialogService;
CustomPopupCommand = new DelegateCommand(OpenClosePopup);
}
private void OpenClosePopup()
{
// It looks like some additional logic should be implemented here.
// How to save previously opened IDialogAware instance and close it if needed?
_dialogService.Show("CustomPopupView", new DialogParameters("Title=Good Title"), result => { });
}
}
CustomPopupViewModel
public class CustomPopupViewModel : BindableBase, IDialogAware
{
private string _title;
public string Title
{
get => _title;
set => SetProperty(ref _title, value);
}
public DelegateCommand<object> CloseCommand { get; }
public CustomPopupViewModel()
{
CloseCommand = new DelegateCommand<object>(CloseDialog);
}
public event Action<IDialogResult> RequestClose;
public void OnDialogOpened(IDialogParameters parameters)
{
Title = parameters.GetValue<string>(nameof(Title));
}
public void OnDialogClosed()
{
}
public bool CanCloseDialog()
{
return true;
}
public void RaiseRequestClose(IDialogResult dialogResult)
{
RequestClose?.Invoke(dialogResult);
}
private void CloseDialog(object button)
{
RaiseRequestClose(
new DialogResult(button is ButtonResult buttonResult ? buttonResult : ButtonResult.Cancel));
}
}
I have no idea how can it be implemented in proper way because method IDialogService.Show() fully decoupled from knowing about ViewModel and View. Of course except the name of View.
You can always send an event through the event aggregator, probably you have to pass some id in the dialog parameters to close the right dialog if there's more than one open at a time.
But this feels really clunky, I'd prefer to get an IDisposable from Show/ShowDialog that closes the dialog on Dispose.
public CustomPopupViewModel(IEventAggregator eventAggregator)
{
eventAggregator.GetEvent<CloseDialogEvent>().Subscribe( id => { if (id == _id) CloseMe(); } );
}
public void OnDialogOpened(IDialogParameters parameters)
{
_id = parameters.GetValue<string>("id");
}
_dialogService.Show("CustomPopupView", new DialogParameters("id=12345"), result => { });
_eventAggregator.GetEvent<CloseDialogEvent>().Publish("12345");
I find it simplest to use Prism implementation of the subscriber pattern
I use a class that will be used in the pattern and is communicated:
public class DialogStatus
{
public bool DialogResult { get; set; }
}
In my sample, I show you how I do this using a Login Dialog in WPF using Prism 8.0.0.1909
in the App.cs
protected override void OnInitialized()
{
var login = Container.Resolve<LoginDialog>();
var result = login.ShowDialog();
if (result.HasValue && result.Value)
{
base.OnInitialized();
}
else
{
Application.Current.Shutdown();
}
}
in LoginDialog.cs in my Dialogs folder
public partial class LoginDialog : Window
{
public LoginDialog(IEventAggregator eventAggregator)
{
InitializeComponent();
eventAggregator.GetEvent<CloseDialogWindowEvent>().Subscribe(OnCloseWindow);
}
private void OnCloseWindow(DialogStatus obj)
{
base.DialogResult = obj.DialogResult;
}
}
now anywhere in my code, in a ViewModel of view a custom control's view model, the only thing I need to do is pass the IEventAggregator in in the constructor and save it in a field.
private readonly IEventAggregator _eventAggregator;
public LoginControlViewModel(IAuthenticationService authenticationService
, IConnectFileImporterService connectFileImporterService
, IDialogService dialogService
, IEventAggregator eventAggregator)
{
_eventAggregator= eventAggregator;
// the other code
}
I can now close my dialog, and in this sample return true to falls to my OnInitalize in my App.cs from anywhere by calling
_eventAggregator.GetEvent<CloseDialogWindowEvent>().Publish(new CloseDialogWindowEvent() { DialogResult = true });
or
_eventAggregator.GetEvent<CloseDialogWindowEvent>().Publish(new CloseDialogWindowEvent() { DialogResult = false});
If i understand correctly, you want to close the dailog window programmatically instead of clicking the windows's close button, right? If It is true, maybe I can provide you with a solution. Although this method is not very elegant, it is very simple.
My project use mahapps styles, I want use metrowindow as the dailoghost window. Following prism documentation, I register dialoghost window and usercontrol like this:
containerRegistry.RegisterDialogWindow<DialogHost>(nameof(DialogHost));
containerRegistry.RegisterDialog<UserEdit, UserEditViewModel>(nameof(UserEdit));
The UserEidt is a usercontrol, I place a confirm button and a cancel button in UserEidt, and both button binding DelegateCommand in UserEditViewModel. The question is, how can i close dailogwindow by clicking the cancel button?
Here is my solution, firstly define a IDailogViewModel interface:
public interface IDialogViewModel
{
Action CloseDialogWindow { get; set; }
}
Then UserEditViewModel implement this interface:
public class UserEditViewModel : BindableBase, IDialogAware,IDialogViewModel
{
public DelegateCommand CancelCmd { get; private set; }
public Action CloseDialogWindow { get; set; }
public UserEditViewModel()
{
CancelCmd = new DelegateCommand(CloseDialogWindow)
}
private void CloseDialogWindow()
{
CloseDialogWindow.Invoke();
}
}
Infact, when the dialog window popup, the UserEdit will be dialogWindow's content. So in the dialogwindow's loaded event handler, i can get the UserEdit object by using Window.Content, here is the code:
public partial class DialogHost : MetroWindow, IDialogWindow
{
public DialogHost()
{
InitializeComponent();
}
public IDialogResult Result { get; set; }
private void MetroWindow_Loaded(object sender, RoutedEventArgs e)
{
var dialogVM = (IDialogViewModel)((UserControl)Content).DataContext;
dialogVM.CloseDialogWindow += CloseDialogWindow;
}
void CloseDialogWindow()
{
Close();
}
}
Now,after clicking the cancel button, the dialogwindow will be close.
I have a method, where I call a new Windows Form in Class A. And in the new Form, I use a Dropdown menu and store the selected Item from the Dropdown in a variable, called selectedItem.Now I have to access this selectedItem in Class A. I use the following code.
public class A
{
public method callingmethod()
{
ExceptionForm expform = new ExceptionForm();
expform.Show();
string newexp = expobj.selectedexception;
}
}
And my code in New Form,
public partial class ExceptionForm : Form
{
public string selectedexception = string.Empty;
private void btnExpSubmit_Click(object sender, EventArgs e)
{
selectedexception = this.comboBox1.GetItemText(this.comboBox1.SelectedItem);
this.Close();
return;
}
}
Now After clicking on Submit button, I get the correct value in selectedItem, But I could not pass it to Class A. How to retun to Class A?
If you are ok with posting ExceptionForm over parent form by disabling it, go for ShowDialog. But, if you do not wish to disable parent for and continue popping ExceptionForm as a new and independent window, try eventing back to parent form. Here I show an example on how to do so:
public partial class ExceptionForm : Form
{
public delegate void SelectValueDelegate(string option);
public event SelectValueDelegate ValueSelected;
private void btnExpSubmit_Click(object sender, EventArgs e)
{
this.Close();
if (this.ValueSelected!= null)
{
this.ValueSelected(this.comboBox1.GetItemText(this.comboBox1.SelectedItem));
}
return;
}
}
And in calling class:
public class A
{
public method callingmethod()
{
ExceptionForm expform = new ExceptionForm();
expform.ValueSelected += ExceptionForm_ValueSelected;
expform.Show();
}
private void ExceptionForm_ValueSelected(string option)
{
string newexp = option;
// Do whatever you wanted to do next!
}
}
Use ShowDialog() method.
expform.ShowDialog();
string newexp = expobj.selectedexception;
When we press the btnSettings, all the user controls properties will be displayed in Property grid. I want display specific properties (only TemperatureValue and TemperatureUnit), is possible? User control code as follows:
using System;
using System.Windows.Forms;
namespace Temperature
{
public partial class temperatureUc : UserControl
{
public enum temperatureUnit
{
Celsius, // default
Delisle, // °De = (100 − °C) * 3⁄2
Fahrenheit, // °F = °C * 9⁄5 + 32
Kelvin, // °K = °C + 273.15
Newton, // °N = °C * 33⁄100
Rankine, // °R = (°C + 273.15) * 9⁄5
Réaumur, // °Ré = °C * 4⁄5
Rømer // °Rø = °C * 21⁄40 + 7.5
}
public temperatureUc()
{
InitializeComponent();
this.cboTemperatureUnit.DataSource = Enum.GetValues(typeof(temperatureUnit));
}
#region "Event"
public delegate void SettingsStateEventHandler(object sender, EventArgs e);
public event SettingsStateEventHandler settingsStateChanged;
private void OnSettingsChanged(object sender, EventArgs e)
{
if (this.settingsStateChanged != null)
this.settingsStateChanged(sender, e);
}
#endregion
#region "Properties"
private Single _TemperatureValue;
public Single TemperatureValue
{
get
{
return this._TemperatureValue;
}
set
{
if (value.GetType() == typeof(Single))
{
_TemperatureValue = value;
this.txtTemperatureValue.Text = _TemperatureValue.ToString();
}
}
}
private temperatureUnit _TemperatureUnit;
public temperatureUnit TemperatureUnit
{
get
{
return this._TemperatureUnit;
}
set
{
if (value.GetType() == typeof(temperatureUnit))
{
_TemperatureUnit = value;
this.cboTemperatureUnit.Text = _TemperatureUnit.ToString();
}
}
}
#endregion
private void btnSettings_Click(object sender, EventArgs e)
{
this.OnSettingsChanged(sender, e);
}
}
}
User control above code will be called from code bellow:
using System;
using System.Windows.Forms;
using Temperature;
using System.Diagnostics;
using System.Drawing;
namespace TemperatureImplements
{
public partial class Form1 : Form
{
private PropertyGrid pGrid = new PropertyGrid();
public Form1()
{
InitializeComponent();
this.temperatureUc1.settingsStateChanged += new temperatureUc.SettingsStateEventHandler(temperatureUc1_settingsStateChanged);
}
void temperatureUc1_settingsStateChanged(object sender, EventArgs e)
{
pGrid.Size = new Size(300, 500);
pGrid.Location = new Point(300,10);
pGrid.SelectedObject = temperatureUc1;
this.Controls.Add(pGrid);
}
}
}
Picture as follows:
There is a way. This article has a section called "Customizing the PropertyGrid Control" that explains how to do it http://msdn.microsoft.com/en-us/library/aa302326.aspx#usingpropgrid_topic5
Basically you just want to define the AppSettings class to only include TemperatureUnit andTemeratureValue`.
AppSettings appset = new AppSettings();
MyPropertyGrid.SelectedObject = appset;
Define AppSettings as follows;
[DefaultPropertyAttribute("SaveOnClose")]
public class AppSettings{
private bool saveOnClose = true;
private string tempUnit;
private int tempValue;
[CategoryAttribute("Global Settings"),
ReadOnlyAttribute(false),
DefaultValueAttribute("Celsius")]
public string TemperatureUnit
{
get { return tempUnit; }
set { tempUnit = value; }
}
[CategoryAttribute("Global Settings"),
ReadOnlyAttribute(false),
DefaultValueAttribute(0)]
public string TemperatureValue
{
get { return tempValue; }
set { tempValue = value; }
}
}
By the way, I'm changing the category from Misc to Global Settings, don't know if that's what you want but it makes sense when they're the only options. You may have to explicitly declare the other attributes this BrowsableAttribute(false) so they're not displayed but I don't think it's necessary.
There might be a way to hide those properties but I think that's the wrong way to go about it.
Instead of passing the user control itself you should create a model with TemperatureUnit and TemperatureValue. Move your defined events to this model.
Then you need to extend a user control which you pass the model to and listens for these events.
Finally set pGrid.SelectedObject to your model and you'll be good to go.
Say I have two user controls and I want to remove an event handler from one instance of the control.
To illustrate I've just made it a button as user control:
public partial class SuperButton : UserControl
{
public SuperButton()
{
InitializeComponent();
}
private void button1_MouseEnter(object sender, EventArgs e)
{
button1.BackColor = Color.CadetBlue;
}
private void button1_MouseLeave(object sender, EventArgs e)
{
button1.BackColor = Color.Gainsboro;
}
}
I've added two super buttons to the form and I want to disable the MouseEnter event firing for SuperButton2.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
superButton2.RemoveEvents<SuperButton>("EventMouseEnter");
}
}
public static class EventExtension
{
public static void RemoveEvents<T>(this Control target, string Event)
{
FieldInfo f1 = typeof(Control).GetField(Event, BindingFlags.Static | BindingFlags.NonPublic);
object obj = f1.GetValue(target.CastTo<T>());
PropertyInfo pi = target.CastTo<T>().GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList list = (EventHandlerList)pi.GetValue(target.CastTo<T>(), null);
list.RemoveHandler(obj, list[obj]);
}
public static T CastTo<T>(this object objectToCast)
{
return (T)objectToCast;
}
}
The code runs but it doesn't work - the MouseEnter and Leave events still fire. I'm looking to do something like this:
superButton2.MouseEnter -= xyz.MouseEnter;
Update: Read this comments questions...
In your case, you don't need to remove all event handlers at once, just the specific one you're interested in. Use -= to remove a handler in the same way you use += to add one:
button1.MouseEnter -= button1_MouseEnter;
Why not just set superButton2.MouseEnter = null;? That should do the trick until somewhere MouseEnter is assigned a value.
Just for an update, another way to handle it, and perfectly legal :)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
namespace TestControls
{
class SimpleButton:Button
{
public bool IgnoreMouseEnter { get; set; }
public SimpleButton()
{
this.IgnoreMouseEnter = false;
}
protected override void OnMouseEnter(EventArgs e)
{
Debug.Print("this.IgnoreMouseEnter = {0}", this.IgnoreMouseEnter);
if (this.IgnoreMouseEnter == false)
{
base.OnMouseEnter(e);
}
}
}
}
There is a check box in the datetimepicker control of winforms .net.
But I could not find the event that is triggered when the check box is checked or unchecked .
Is there a way out?
It does however trigger the value changed event
You´ll have to store the old "checked" value in order to compare to the new one, so you´ll be able to determine if the "checked" state has changed:
bool oldDateChecked = false; //if it's created as not checked
private void dtp_filtro_date_ValueChanged(object sender, EventArgs e)
{
if (this.dtp_filtro_date.Checked != oldDateChecked)
{
oldDateChecked = this.dtp_filtro_date.Checked;
//do your stuff ...
}
}
Run into the same issue. I needed a CheckedChangedEvent on a winforms DateTimePicker control. So with the inspiration of the answers before me I created an inherited User Control named DateTimePicker2, inheriting from DateTimePicker that implements this event. It looks like it works but no guarantees.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace MyNameSpace
{
public partial class DateTimePicker2 : DateTimePicker
{
private bool _checked;
public new bool Checked
{
get
{
return base.Checked;
}
set
{
if (value != base.Checked)
{
base.Checked = value;
_checked = base.Checked;
OnCheckedChanged(new CheckedChangedEventArgs { OldCheckedValue = !value, NewCheckedValue = value });
}
}
}
public event CheckedChangedEventHandler CheckedChanged;
public DateTimePicker2()
{
InitializeComponent();
_checked = Checked;
}
protected virtual void OnCheckedChanged(CheckedChangedEventArgs e)
{
if (CheckedChanged != null) CheckedChanged(this, e);
}
private void DateTimePicker2_ValueChanged(object sender, EventArgs e)
{
if (Checked != _checked)
{
_checked = Checked;
CheckedChangedEventArgs cce = new CheckedChangedEventArgs { OldCheckedValue = !_checked, NewCheckedValue = _checked };
OnCheckedChanged(cce);
}
}
}
public delegate void CheckedChangedEventHandler(object sender, CheckedChangedEventArgs e);
public class CheckedChangedEventArgs : EventArgs
{
public bool OldCheckedValue { get; set; }
public bool NewCheckedValue { get; set; }
}
}
And off-course don't forget to subscribe to the DateTimePicker2_ValueChanged event from the designer.
The reason why I used both a new Checked property (to hide the base.Checked one) and a _checked field to keep truck of the old value, is because
the base.Checked property does not fire the ValueChanged event when changed programmatically and therefore needed a new property that could do that.
the this.Checked new property does not fire the ValueChanged event when changed from the UI and therefore needed a flag that would track the base.Checked property.
Basically a combination of both approaches was needed.
I hope this helps.
I know this is super old but this could help someone.
You can capture DataTimePicker.MouseUp event
private void dateTimePicker1_MouseUp(object sender, MouseEventArgs e)
{
if (((DateTimePicker)sender).Checked)
{
//Do whatever you need to do when the check box gets clicked
}
else
{
//Do another stuff...
}
}
You will need to do the same with KeyUp event in order to get the Space key press that could also activate the checkbox.