How to create property simmilar to checkBox checked? - c#

IDE: vs 2010, c# .net , winforms
Hi, I am creating a toggle button userControl which will be having ON and OFF state, also I have created a property which set the initial state of the button, (SAME as checkbox IsChecked).
public bool Checked
{
get { return isToggleOn; }
set { isToggleOn = value;
onPropertyChanged();
}
}
private void onPropertyChanged()
{
this.BackgroundImage = isToggleOn ? Properties.Resources.toggleOnMIUI : Properties.Resources.toggleOffMIUI;
IsChecked = isToggleOn ? true : false;
}
This is working, in design time, Now in form1 I have added this control and created OnClickEvent to check the current state:
private void ucTglOverStepping_Click(object sender, EventArgs e)
{
if (ucTglOverStepping.Checked)
{
doWork = true; //do work is bool variable in form1.cs
}
else if (!ucTglOverStepping.Checked)
{
doWork = false;
}
}
Now the problem is when the toggle button is in on state it is going into else condition.
because onPropertyChanged() is executing before this ucTglOverStepping_Click(object sender, EventArgs e) function, is there any way to execute the onPropertyChanged() after the execution of ucTglOverStepping_Click() function, or any other technique to fix this issue.
PROBLEM Explanation:
Test Case 1: suppose the current state is ON
user clicked on Button -> it will go in off state (checked should become false, it should go in else condition of OnClickEvent. but checked is getting true.)

you need to call function in set;get; of property
public bool Checked
{
get
{
return isToggleOn;
}
set
{
onPropertyChanged();
isToggleOn = value;
}
}
If onPropertyChanged() only call when you set/change value or Checked properties ..
In your function seems you set property again at last line .. so it call function recursive.. you not need to set IsChecked value again
private void onPropertyChanged()
{
this.BackgroundImage = isToggleOn ? Properties.Resources.toggleOnMIUI : Properties.Resources.toggleOffMIUI;
// not need this
// IsChecked = isToggleOn ? true : false;
}
and your Question is
private void ucTglOverStepping_Click(object sender, EventArgs e)
{
if (ucTglOverStepping.Checked)
{
doWork = true; //do work is bool variable in form1.cs
}
else if (!ucTglOverStepping.Checked)
{
doWork = false;
}
}
when you get property value it run only first part it will not go to call
`onPropertyChanged()'
it will run only
public bool Checked
{
get
{
return isToggleOn;
}
}
UPDATE
it it sill confusing ..
but why you not use CheckBox1_CheckedChanged Event ?

Related

C# set get not working first click using forms

I am building a simple text editor program. When the text has been edited a window shows up and asking if you want to save the file. This has been implemented using forms and booleans stored in the popup window.
If i press yes, no or cancel the first time the setters doesn't work. But if i press the second the setters work perfectly. Why doesn't it work the first time?
Code for the popup window.
public bool Exit { set; get; }
public bool Save { set; get; }
public bool _Open { set; get; }
case ("Yes"):
{
if (form1.Operation() == "Exit")
{
Exit = true; //Bool that tells the program to exit.
MessageBox.Show(Exit.ToString()); //This prints the correct value
}
Save = true; //Bool that states if file should be saved.
return;
}
Code for main textedit window. Example with a user pressed the exit button.
public partial class Form1 : Form
{
Form3 form3 = new Form3(); //the popup class.
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
if (_edited)
{
_operation = "Exit";
form3.ShowDialog();
MessageBox.Show(form3.Exit.ToString()); //Always false on first entry?
if (form3.Exit)
{
if (form3.Save)
{
Save();
}
}
else
{
e.Cancel = true;
}
}
}
}
}
In your question you say "yes, no or cancel" but in the second block of code it seems to only set Exit = true for the "Yes" case. Could this be the issue?
You could try setting breakpoints and debugging the code to be able to figure what is going on. If you remove auto properties and add backing field manually, you could also set breakpoints to see when the setters are called. You could write your Exit like this:
private bool _exit;
public bool Exit
{
get => _exit;
set
{
_exit = value;
}
}
This would allow you to set a breakpoint and see exactly when the setter is called.
If you need help with debugging, please provide some information about which code editor you are using.

GetTemplateChild("CompactOverlayButton") as Button returns Null

I am creating custom transport controls.
I want to have a Visibility control for a custom Button which I have created. So I have created a Property for it. In that Property, I have used GetTemplateChild("CompactOverlayButton") as Button to get the particular button but it returns null.
Here is my code
public bool IsCompactOverlayButtonVisible
{
get
{
var compactOverlayButton = GetTemplateChild("CompactOverlayButton") as Button;
if (compactOverlayButton.Visibility == Visibility.Visible)
return true;
else
return false;
}
set
{
var compactOverlayButton = GetTemplateChild("CompactOverlayButton") as Button;
if (value)
compactOverlayButton.Visibility = Visibility.Visible;
else
compactOverlayButton.Visibility = Visibility.Collapsed;
}
}
But the same line of code returns proper value in OnApplyTemplate() function.
Here is my code for OnApplyTemplate()
protected override void OnApplyTemplate()
{
var compactOverlayButton = GetTemplateChild("CompactOverlayButton") as Button;
compactOverlayButton.Click += CompactOverlayButton_ClickAsync;
base.OnApplyTemplate();
}
IsCompactOverlayButtonVisible probably gets evaluated for the first time before OnApplyTemplate(), meaning that the first time it gets evaluated, the template hasn't been applied and the button doesn't exist yet. In OnApplyTemplate(), get the button and assign it to a private field.
private Button _compactOverlayButton;
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
_compactOverlayButton = GetTemplateChild("CompactOverlayButton") as Button;
_compactOverlayButton.Click += CompactOverlayButton_ClickAsync;
}
And before you try to touch the button's properties, make sure it's not null.
public bool IsCompactOverlayButtonVisible
{
get
{
return _compactOverlayButton != null
&& _compactOverlayButton.Visibility == Visibility.Visible;
}
set
{
if (_compactOverlayButton != null)
{
compactOverlayButton.Visibility = value
? Visibility.Visible
: Visibility.Collapsed;
}
}
}
If something will set this value before the template is applied, for example if it's a public property of the control that may be set in XAML (which it sure looks like it is), you can't do it this way. You need to make it a regular dependency property, give it a PropertyChanged handler that updates the button's visibility if the button exists, and add a line in OnApplyTemplate() to update the actual button when you get your hands on it. Then it'll be usable as a target of a binding as well.
Update
And here's how you do that. This is the right way.
private Button _compactOverlayButton;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_compactOverlayButton = GetTemplateChild("CompactOverlayButton") as Button;
// Update actual button visibility to match whatever the dependency property value
// is, in case XAML gave us a value for it already.
OnIsCompactOverlayButtonVisibleChanged();
_compactOverlayButton.Click += CompactOverlayButton_Click;
// Secondly, just in case something in the XAML may change the button's visibility,
// put a watch on the property and update our dependency property to match when that
// changes.
var dpd = DependencyPropertyDescriptor.FromProperty(Button.VisibilityProperty, typeof(Button));
dpd.AddValueChanged(_compactOverlayButton, CompactOverlayButton_VisibilityChanged);
}
protected void CompactOverlayButton_VisibilityChanged(object sender, EventArgs args)
{
IsCompactOverlayButtonVisible = _compactOverlayButton.Visibility == Visibility.Visible;
}
private void CompactOverlayButton_Click(object sender, RoutedEventArgs e)
{
// ...whatever
}
#region IsCompactOverlayButtonVisible Property
public bool IsCompactOverlayButtonVisible
{
get { return (bool)GetValue(IsCompactOverlayButtonVisibleProperty); }
set { SetValue(IsCompactOverlayButtonVisibleProperty, value); }
}
public static readonly DependencyProperty IsCompactOverlayButtonVisibleProperty =
DependencyProperty.Register(nameof(IsCompactOverlayButtonVisible), typeof(bool), typeof(CustomMediaTransportControls),
new FrameworkPropertyMetadata(true,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
IsCompactOverlayButtonVisible_PropertyChanged));
protected static void IsCompactOverlayButtonVisible_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// It's a hassle to do stuff in a static method, so my dependency property
// snippet just creates a private instance method and calls it from the
// static handler.
(d as CustomMediaTransportControls).OnIsCompactOverlayButtonVisibleChanged();
}
private void OnIsCompactOverlayButtonVisibleChanged()
{
if (_compactOverlayButton != null)
{
// If the existing value is the same as the new value, this is a no-op
_compactOverlayButton.Visibility =
IsCompactOverlayButtonVisible
? Visibility.Visible
: Visibility.Collapsed;
}
}
#endregion IsCompactOverlayButtonVisible Property

C# - Check and notify of boolean value changed

Good day all
I am struggling to familiarize myself with Events, I just can't seem to get my head around the concept, after quite a few tutorials and videos, I am still lost, thus I will be specific about my problem.
In short, a thread pings an IP, if the ping reports a success,
ServerOnline = (ping == success ) ? true : false;
The event listener should "listen" for a variable change, get the variable and process accordingly.
A example based on the code below would be of an immense help.
class tcp_connector
{
bool ServerOnline
void thread_checkServer()
{
//do code
ServerOnline = true;
//notify of variable change
}
}
class tcp_sender
{
//when ServerOnline bool = true
//button.color = color.green;
}
You can declare an event in your tcp_connector that you raise if ServerOnline changes like this:
class tcp_connector
{
// standard event pattern
public event EnventHandler ServerOnlineChanged;
protected virtual void OnServerOnlineChanged
{
EventHandler handler = ServerOnlineChanged; // for thread safety
if (handler != null)
handler(this, EventArgs.Empty);
}
private bool _serverOnline;
public bool ServerOnline // implement as property
{
get { return _serverOnline; }
set {
if (_serverOnline == value) return;
_serverOnline = value;
OnServerOnlineChanged(); // raise event
}
}
void thread_checkServer()
{
//do code
// be sure to use the property ServerOnline, not the
// field _serverOnline!
// the property setter will raise ServerOnlineChangedEvent
ServerOnline = true;
}
}
And then you can consume that event in your tcp_sender like this:
class tcp_sender
{
private tcp_connector _connector;
public tcp_sender()
{
_connector = new tcp_connector();
// subscribe to event
_connector.ServerOnlineChanged += tcp_connector_ServerOnlineChanged;
}
// the event handler for the ServerOnlineChanged event
private void tcp_connector_ServerOnlineChanged(object sender, EventArgs e)
{
if (_connector.ServerOnline)
button.color = color.green;
}
}
This is not really a "listener" that watches your property ServerOnline, but by seperating it into a field and a property you can recognize changes and raise the specified event.

How to cancel a ComboBox SelectionChanged event?

Is there an easy method to prompt the user to confirm a combo box selection change and not process the change if the user selected no?
We have a combo box where changing the selection will cause loss of data. Basically the user selects a type, then they are able to enter attributes of that type. If they change the type we clear all of the attributes as they may no longer apply. The problem is that to under the selection you raise the SelectionChanged event again.
Here is a snippet:
if (e.RemovedItems.Count > 0)
{
result = MessageBox.Show("Do you wish to continue?",
"Warning", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.No)
{
if (e.RemovedItems.Count > 0)
((ComboBox)sender).SelectedItem = e.RemovedItems[0];
else
((ComboBox)sender).SelectedItem = null;
}
}
I have two solutions, neither of which I like.
After the user selects 'No', remove the SelectionChanged event handler, change the selected item and then register the SelectionChanged event handler again. This means you have to hold onto a reference of the event handler in the class so that you can add and remove it.
Create a ProcessSelectionChanged boolean as part of the class. Always check it at the start of the event handler. Set it to false before we change the selection back and then reset it to true afterwards. This will work, but I don't like using flags to basically nullify an event handler.
Anyone have an alternative solution or an improvement on the ones I mention?
I found this good implementation.
private bool handleSelection=true;
private void ComboBox_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
if (handleSelection)
{
MessageBoxResult result = MessageBox.Show
("Continue change?", MessageBoxButton.YesNo);
if (result == MessageBoxResult.No)
{
ComboBox combo = (ComboBox)sender;
handleSelection = false;
combo.SelectedItem = e.RemovedItems[0];
return;
}
}
handleSelection = true;
}
source: http://www.amazedsaint.com/2008/06/wpf-combo-box-cancelling-selection.html
Maybe create a class deriving from ComboBox, and override the OnSelectedItemChanged (Or OnSelectionChangeCommitted.)
Validating within the SelectionChanged event handler allows you to cancel your logic if the selection is invalid, but I don't know of an easy way to cancel the event or item selection.
My solution was to sub-class the WPF combo-box and add an internal handler for the SelectionChanged event. Whenever the event fires, my private internal handler raises a custom SelectionChanging event instead.
If the Cancel property is set on the corresponding SelectionChangingEventArgs, the event isn't raised and the SelectedIndex is reverted to its previous value. Otherwise a new SelectionChanged is raised that shadows the base event. Hopefully this helps!
EventArgs and handler delegate for SelectionChanging event:
public class SelectionChangingEventArgs : RoutedEventArgs
{
public bool Cancel { get; set; }
}
public delegate void
SelectionChangingEventHandler(Object sender, SelectionChangingEventArgs e);
ChangingComboBox class implementation:
public class ChangingComboBox : ComboBox
{
private int _index;
private int _lastIndex;
private bool _suppress;
public event SelectionChangingEventHandler SelectionChanging;
public new event SelectionChangedEventHandler SelectionChanged;
public ChangingComboBox()
{
_index = -1;
_lastIndex = 0;
_suppress = false;
base.SelectionChanged += InternalSelectionChanged;
}
private void InternalSelectionChanged(Object s, SelectionChangedEventArgs e)
{
var args = new SelectionChangingEventArgs();
OnSelectionChanging(args);
if(args.Cancel)
{
return;
}
OnSelectionChanged(e);
}
public new void OnSelectionChanged(SelectionChangedEventArgs e)
{
if (_suppress) return;
// The selection has changed, so _index must be updated
_index = SelectedIndex;
if (SelectionChanged != null)
{
SelectionChanged(this, e);
}
}
public void OnSelectionChanging(SelectionChangingEventArgs e)
{
if (_suppress) return;
// Recall the last SelectedIndex before raising SelectionChanging
_lastIndex = (_index >= 0) ? _index : SelectedIndex;
if(SelectionChanging == null) return;
// Invoke user event handler and revert to last
// selected index if user cancels the change
SelectionChanging(this, e);
if (e.Cancel)
{
_suppress = true;
SelectedIndex = _lastIndex;
_suppress = false;
}
}
}
In WPF dynamically set the object with
if (sender.IsMouseCaptured)
{
//perform operation
}
I do not believe using the dispatcher to post (or delay) a property update is a good solution, it is more of a workaround that is not really needed. The following solution i fully mvvm and it does not require a dispatcher.
First Bind the SelectedItem with an Explicit binding Mode. //this enables us to decide whether to Commit using the UpdateSource() method the changes to the VM or to Revert using the UpdateTarget() method in the UI.
Next, add a method to the VM that confirms if the change is allowed (This method can contain a service that prompts for user confirmation and returns a bool).
In the view code behind hook to the SelectionChanged event and update the Source (i.e., the VM) or the Target (i.e. the V) in accordance to whether the VM.ConfirmChange(...) method returned value as follows:
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(e.AddedItems.Count != 0)
{
var selectedItem = e.AddedItems[0];
if (e.AddedItems[0] != _ViewModel.SelectedFormatType)
{
var comboBoxSelectedItemBinder = _TypesComboBox.GetBindingExpression(Selector.SelectedItemProperty); //_TypesComboBox is the name of the ComboBox control
if (_ViewModel.ConfirmChange(selectedItem))
{
// Update the VM.SelectedItem property if the user confirms the change.
comboBoxSelectedItemBinder.UpdateSource();
}
else
{
//otherwise update the view in accordance to the VM.SelectedItem property
comboBoxSelectedItemBinder.UpdateTarget();
}
}
}
}
This is an old question, but after struggling with the issue time and again I came up with this solution:
ComboBoxHelper.cs:
public class ComboBoxHelper
{
private readonly ComboBox _control;
public ComboBoxHelper(ComboBox control)
{
_control = control;
_control.PreviewMouseLeftButtonDown += _control_PreviewMouseLeftButtonDown; ;
_control.PreviewMouseLeftButtonUp += _control_PreviewMouseLeftButtonUp; ;
}
public Func<bool> IsEditingAllowed { get; set; }
public Func<object, bool> IsValidSelection { get; set; }
public Action<object> OnItemSelected { get; set; }
public bool CloseDropDownOnInvalidSelection { get; set; } = true;
private bool _handledMouseDown = false;
private void _control_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var isEditingAllowed = IsEditingAllowed?.Invoke() ?? true;
if (!isEditingAllowed)
{
e.Handled = true;
return;
}
_handledMouseDown = true;
}
private void _control_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (!_handledMouseDown) return;
_handledMouseDown = false;
var fe = (FrameworkElement)e.OriginalSource;
if (fe.DataContext != _control.DataContext)
{
//ASSUMPTION: Click was on an item and not the ComboBox itself (to open it)
var item = fe.DataContext;
var isValidSelection = IsValidSelection?.Invoke(item) ?? true;
if (isValidSelection)
{
OnItemSelected?.Invoke(item);
_control.IsDropDownOpen = false;
}
else if(CloseDropDownOnInvalidSelection)
{
_control.IsDropDownOpen = false;
}
e.Handled = true;
}
}
}
It can be used in a custom UserControl like this:
public class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
var helper = new ComboBoxHelper(MyComboBox); //MyComboBox is x:Name of the ComboBox in Xaml
helper.IsEditingAllowed = () => return Keyboard.Modifiers != Modifiers.Shift; //example
helper.IsValidSelection = (item) => return item.ToString() != "Invalid example.";
helper.OnItemSelected = (item) =>
{
System.Console.WriteLine(item);
};
}
}
This is independent of the SelectionChanged event, there are no side effects of the event firing more often than required. So others can safely listen to the event, e.g. to update their UI. Also avoided: "recursive" calls caused by resetting the selection from within the event handler to a valid item.
The assumptions made above regarding DataContext may not be a perfect fit for all scenarios, but can be easily adapted. A possible alternative would be to check, if the ComboBox is a visual parent of e.OriginalSource, which it isn't when an item is selected.

How to mark C# event handler as "handled"?

Say I've got a button on a form that I want to disable if some condition is met. Is there a way to check for this condition inside the button's "IsEnabled" event handler and modify the enabled state such that setting the enabled state a second time does not trigger another call to the IsEnabled event handler?
Let me demonstrate:
private void ExportResults_IsEnabledChanged (object sender, DependencyPropertyChangedEventArgs e)
{
if (some condition)
{
uxExportResults.IsEnabled = false; // this will cause another call to the event handler, eventually resulting in a stack overflow
}
}
Assume I'm triggering the event elsewhere (which I am).
if (someCondition && uxExportResults.IsEnabled) { ... }
This will only disable your control if it's enabled.
Another option is to temporarily disable the event like so:
private void ExportResults_IsEnabledChanged (object sender, DependencyPropertyChangedEventArgs e)
{
if (some condition)
{
uxExportResults.IsEnabledChanged -= ExportResults_IsEnabledChanged;
try
{
uxExportResults.IsEnabled = false; // this will cause another call to the event handler, eventually resulting in a stack overflow
}
finally
{
uxExportResults.IsEnabledChanged += ExportResults_IsEnabledChanged;
}
}
}
Simplest solution is to check the value of IsEnabled before you set it.
private void ExportResults_IsEnabledChanged (object sender, DependencyPropertyChangedEventArgs e)
{
if (uxExportResults.IsEnabled == true)
{
uxExportResults.IsEnabled = false;
}
}
Also, if you have the ability to change the code for the button, IsEnabled should not send the event unless the value actually changes.
public bool IsEnabled
{
get { return isEnabled; }
set
{
if(isEnabled != value)
{
isEnabled = value;
IsEnabledChanged(this,args);
}
}
}

Categories