WPF if statement based on radio button checked or not - wpf

I'm having difficulty with something which seems like it should be very simple. I'm coming from Windows Forms and starting up with WPF. I think I have a simple syntax issue but I can't seem to find a specific example for this radio button issue.
I have a radio button on my GUI for a query to run either via a map selection or a list. When load is clicked, it should perform one operation if map is selected, a different operation for list. Code looks similar to this:
private void Load_Click(object sender, RoutedEventArgs e)
{
if (rdBtnList.Checked == true)
{
//do this
}
// if rdBtnList not checked (ie if rdBtnMap is checked)
// do this
}
Any help would be greatly appreciated. Thanks.

Change:
if (rdBtnList.Checked == true)
to
if (rdBtnList.IsChecked == true)
Note:
I'm coming from Windows Forms and starting up with WPF
You must forget everything you ever learned in winforms, and embrace MVVM. You should create a ViewModel and bind your rdBtnList.IsChecked property to some boolean value in the ViewModel, then perform your logic in there. The views' code behind is not the right place for application logic.

The property in WPF is called IsChecked hence you just need to update the if statement
if (rdBtnList.IsChecked == true) {
...
}
The IsChecked property is bool? (nullable value) in WPF so you may choose to be more explicit here by doing the following
if (rdBtnList.IsChecked.HasValue && rdBtnList.IsChecked.Value) {
...
}

Are you missing a return statement? As is, the posted code will always execute the if not checked path:
private void Load_Click(object sender, RoutedEventArgs e)
{
if (rdBtnList.IsChecked == true)
{
//do this
return; // return, or the if not checked path below will always win.
}
// if rdBtnList not checked (ie if rdBtnMap is checked)
// do this
}
In WPF, you'll need to use the IsChecked property. See the RadioButton Class for more details.

Related

WPF Combobox SelectionChanged event

I have a project where I have a ComboBox interacting with 2 Sliders.
Each o these 3 controls are triggered by events: namely a SelectionChanged for the ComboBox and a ValueChanged for the 2 sliders.
I thought the ValueChanged event was giving me problems, not updating the values and min/max accordingly to the combobox selection. For some reason I assumed that maybe both events were triggered simultaneously resulting in a mix up of my variables.
Well, I decided to change my ValueChanged events so that they only update a label so see if that fixed the problem. It did not.
In other words, the SelectionChanged event is where my problem is lying.
Looking through my code I didn't see any problem, and at this point the only thing I can think of is as follow:
private void chanList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
switch (chanList.Text)
{
case "Channel 1":
{ // blablabla }
}
}
Reason for the problem is that chanList.Text doesn't contain the "new selection", it still contains the old one. I am guessing that when the "SelectionChanged" event is triggered, the content of the of combobox (in my case chanList.Text) is not yet "updated". So it results in picking up the wrong case in my switch.
Now my questions:
1. are my assumptions correct?
2. if so, what should I replace my Switch test by? Assuming chanList.Text isn't updated yet, maybe going with something chanList.SelectedItem should be the way to go. However, I was able to find the correct verbose to access the content (text) of the selected item... That's why I was going with chanList.Text which has been working fine well at least until I started using that event.
Thanks for the help!
Steve
The combobox text will not be changed in the SelectionChanged event handler. The selections will be found in the SelectionChangedEventArgs object. Specifically e.AddedItems. It is possible, although perhaps not for your control, that the user will have selected multiple items in the combobox, so e.AddedItems is a list. Scroll through the list and make the updates necessary.
private void chanList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (object item in e.AddedItems)
{
if (item is string)
{
switch (item as string)
{
case "Channel 1":
Console.WriteLine("Channel 1");
break;
default:
break;
}
}
}
}

WPF ListBox - Detect Scrollbar Up/Down Button click

I'm using a listbox to display a list of numeric values where the selected value get applied to an object and is saved. I had the idea to set the height on on the listbox to be just enough to display a single row. Of course this causes the vertical scollbar to appear, and without a scroll body, which is exactly what I was looking for.
When I click the up/down arrow on the list box scrollbar, it scrolls the next item into view correctly. However, the item is not selected. I immediately had problems with the actual selected value being saved and not the value that is in view. This is because it requires a click on the number after the scrollbar button to actually select the value, which is not very intuitive.
So, after some intel-sense hunting, I began search for possible ways to increment the selected value with clicks on the listbox scrollbar buttons, but I can't find anything that actually uses them.
I have found posts that describe actions when clicking on the scroll bar body, and still others that use designer added buttons to perform the index change on the listbox. However, the scroll bar body is not visible do to the short height of the list box and it seems silly to add more buttons when I already have those two available and doing most of the work.
I have also found a post that described something similar, but using a listview. But, I would hate to have to swap the control out at this point for one feature I really think should be available somewhere on the control I'm currently using.
So, I guess what I'm looking for is a way to address the click event handler of the vertical scrollbar buttons on a listbox control.
Any help would be greatly appreciated.
(Thanks all, for the 1000 other things I didn't have to post to solve here!)
I had heard about that Phil, and your right. I'm doing a replacement for the numeric up-down.
I just figured that there was probably a viable alternative, since that specific control was not originally part of the framework. I had also gotten much of it working and really like the results, and the way it picked up the theme.
Since the core of this application will become a start point for future applications, I wanted to include this functionality and was prepared to do a little work for it.
What I ended up doing was a little complicated that it was worth, but made easy with a useful helper function. I needed to search the 'Visual Tree' for my target type. From there I was able to access enough functionality to finish up.
First:
Using a helper function I found here (Thanks Bruno) I was able to add this to my Loaded event:
private Double currentVerticalOffset;
private void Page_Loaded_1(object sender, RoutedEventArgs e)
{
ScrollViewer sv = Helpers.ViewHelpers.ListBoxHelper.FindVisualChild<ScrollViewer>(listbox);
sv.ScrollChanged += HandleRankScrollChange;
currentVerticalOffset = sv.VerticalOffset;
}
Then, I handle the scroll changed event:
private void HandleRankScrollChange(object sender, ScrollChangedEventArgs e)
{
ScrollViewer sv = Helpers.ViewHelpers.ListBoxHelper.FindVisualChild<ScrollViewer>(listbox);
if (sv.VerticalOffset > currentVerticalOffset)
{
Helpers.ViewHelpers.ListBoxHelper.SelectNextItem(listbox);
}
if (sv.VerticalOffset < currentVerticalOffset)
{
Helpers.ViewHelpers.ListBoxHelper.SelectPreviousItem(listbox);
}
currentVerticalOffset = sv.VerticalOffset;
}
The helpers I call here are pretty simple, but again, this will become a foundation kit, so having the methods will probably come in handy again.
public static void SelectNextItem(ListBox lb)
{
if (lb.SelectedIndex < lb.Items.Count)
{
lb.SelectedIndex++;
}
}
public static void SelectPreviousItem(ListBox lb)
{
if (lb.SelectedIndex > 0)
{
lb.SelectedIndex--;
}
}
Bruno's helper function
public static childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
{
return (childItem)child;
}
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
{
return childOfChild;
}
}
}
return null;
}
Thanks again.

How to prevent key entry with NumericUpDown control?

I have a numericupdown control on a winform and I noticed while testing that not only you have the option of changing the value by pressing up and down key but also simply entering the values from your keyboard.
I don't want that. I only want the user to be able to change the numericupdown's value only by clicking the up and down buttons within the box.
So far I simply can't find a solution.
Does anyone know how to do this?
To disable user from editing, set Readonly property to true.
updown.ReadOnly = true;
For more tailoring, you may refer this answer.
Sounds bad for user experience depending on the range of values you are allowing.
To do this you need to create a control with inherits from NumericUpDown and override the OnKeyPress/OnKeyDown methods.
Using updown.ReadOnly = True; does not work for me. It seems to be a reoccuring bug.
But catching any changes and then undo it does. For this bind the function updown_ValueChanged() to the updown.ValueChanged attribute.
decimal spin = 1;
private void updown_ValueChanged(object sender, EventArgs e)
{
if (updown.ReadOnly)
{
if (updown.Value != spin)
{
updown.Value = spin;
}
}
else spin = updown.Value;
}

Is there a way to cancel TabControl.Items.CurrentChanging?

There is unfortunately no TabControl.SelectionChanging event (Selector.SelectionChanging), I am struggling to implement this behavior so I can cancel the changing request.
I tried to handle the TabControl.Items.CurrentChanging (the Items property is and ItemCollection) event setting e.Cancel (of the CurrentChangingEventArgs) to true, but the UI is is updated with the new tab although the item is not changed in the collection.
Is there any way to prevent user from switching to a different TabItem when a condition is dissatisfied?
I don't know the exact reason why this happens, and it annoys me greatly.
But here's my workaround for it:
In the sample below, checkbox is "locking" the current tab. So checked means user can't change tab.
void Items_CurrentChanging(object sender, CurrentChangingEventArgs e)
{
if (checkBox1.IsChecked.Value)
{
var item = ((ICollectionView)sender).CurrentItem;
e.Cancel = true;
tabControl1.SelectedItem = item;
}
}
Basically, what happens is (if I understand this correctly) the visual tree gets updated, but the logical tree does not. The above way forces the visual to sync with the logical tree.
You can also handle the PreviewLostKeyboardFocus event on each TabItem, and set the Handled property of the event arguments to true to prevent switching to another tab:
protected void tabItem_PreviewLostKeyboardFocus(object sender,
KeyboardFocusChangedEventArgs e)
{
if (!ValidateTabItem((TabItem) sender)) {
e.Handled = true;
}
}
See http://www.netframeworkdev.com/windows-presentation-foundation-wpf/how-to-cancel-navigation-between-tabitems-in-a-tabcontrol-84994.shtml.

WinForms TabControl validation: Switch to a tab where validation failed

I currently have a Form with a TabControl containing some TabPages. Each TabPage has several controls with validation logic and appropriate ErrorProviders. On my OK_Button_Clicked event, I call Form.ValidateChildren() in order to determine whether to save and close the form . Now, suppose I have a control in tab 1 that fails validation, but the currently visible tab is tab 2. When the user presses OK, he would get no visual indication as to why the form is not closing. I would like to be able to automatically switch to a tab where validation failed, so the user would see the ErrorProvider's indication of error.
One approach would be subscribing to the Validated and validating events of all appropriate controls, and knowing which tab each of them is in, whenever one fails validation, a list of tabs that failed validation could be built. Since no ValidationFailed event is generated as far as I know, this could be cumbersome (e.g. defining a boolean for each control, setting it to false before validation and to true on its Validated event). And even if I had such an event, I would be forced to listen to many validation events, one for each control that might fail validation, and maintain the list of unvalidated tabs in code. I should note here that subscribing directly to the TabPage's validation events doesn't work, because they pass as validated even if controls contained inside them fail validation.
Another approach could leverage the fact that the controls in my TabPage happen to be custom controls. I could then make them implement an interface such as:
interface ILastValidationInfoProvider
{
public bool LastValidationSuccessful {get; set;}
}
For example:
public MyControl : UserControl, ILastValidationInfoProvider
{
MyControl_Validing(object sender, object sender, CancelEventArgs e)
{
if (this.PassesValidation())
this.ErrorProvider.SetError(sender, null);
LastValidationSuccessful = true;
else
e.Cancel = true;
this.ErrorProvider.SetError("Validation failed!", null);
LastValidationSuccessful = false;
}
}
And then, after the call to ValidateChildren I could use code such as:
public void OK_Button_Click
{
if (form.ValidateChildren())
this.Close()
else
foreach (TabPage tab in this.TabControl)
foreach (Control control in tab.Controls)
{
ValidationInfo = control as ILastValidationInfoProvider
if (ValidationInfo != null && !ValidationInfo.LastValidationSuccessful)
{
this.TabControl.SelectTab(tab);
return;
}
}
}
I like this approach better but it doesn't cater to cases where the controls being validated are not custom.
I would gladly use a better approach. Any ideas?
EDIT I am using Form.AutoValidate = EnableAllowFocusChange (as recommended by Chris Sells in his WinForms book), So the focus can indeed change from controls that failed validation (including moving to other tabs). I have also updated the sample code for the custom control to emphasize the fact that the ErrorProvider resides internally inside it.
OK so I finally figured it out.
I keep a dictionary whose keys are the TabPages and the values are HashSets of unvalidated controls within the corresponding tab. This is easily done by subscribing to all the validating and validated events of the controls in each tab. Finally, in OK_BUtton_Click, if ValidateChildren fails, I know one of the hashsets will be none empty and I simply jump to the first unvalidated tab (only if the currently selected tab doesn't have any error itself).
Dictionary<TabPage, HashSet<Control>> _tabControls
= new Dictionary<TabPage, HashSet<Control>>();
public OptionsForm()
{
InitializeComponent();
RegisterToValidationEvents();
}
private void RegisterToValidationEvents()
{
foreach (TabPage tab in this.OptionTabs.TabPages)
{
var tabControlList = new HashSet<Control>();
_tabControls[tab] = tabControlList;
foreach (Control control in tab.Controls)
{
var capturedControl = control; //this is necessary
control.Validating += (sender, e) =>
tabControlList.Add(capturedControl);
control.Validated += (sender, e) =>
tabControlList.Remove(capturedControl);
}
}
}
private void Ok_Button_Click(object sender, EventArgs e)
{
if (this.ValidateChildren())
{
_settings.Save();
this.Close();
}
else
{
var unvalidatedTabs = _tabControls.Where(kvp => kvp.Value.Count != 0)
.Select(kvp => kvp.Key);
TabPage firstUnvalidated = unvalidatedTabs.FirstOrDefault();
if (firstUnvalidated != null &&
!unvalidatedTabs.Contains(OptionTabs.SelectedTab))
OptionTabs.SelectedTab = firstUnvalidated;
}
}
I think it's pretty sweet !

Resources