I have two toggleButton's, both set to null state, now I want to check if the user ever toggled the button or not.
<StackPanel Height="45" Orientation="Horizontal" Margin="0,20,0,0">
<Label VerticalContentAlignment="Bottom" Content="هل القاعة صالحة من حيث الإستماع؟"/>
<ToggleButton x:Name="ListeningStatusText" IsThreeState="True" IsChecked="{x:Null}" />
</StackPanel>
<StackPanel Height="45" Orientation="Horizontal" Margin="0,20,0,0">
<Label VerticalContentAlignment="Bottom" Content="الإضاءة"/>
<ToggleButton x:Name="LightingStatusText" IsThreeState="True" IsChecked="{x:Null}" />
</StackPanel>
With the code bellow I failled to see if the user never toggle the button:
var ToggleButtonControl = (ToggleButton)ControlName; //
if (ToggleButtonControl.IsChecked == null)
{
parameterStr = "/////////////";
//MessageBox.Show("This should pop-up only if the user never toggled the button");
}
else
{
//MessageBox.Show("The user toggle or toggle it back");
if (ToggleButtonControl.IsChecked == false)
parameterStr = "لا";
else
parameterStr = "نعم";
}
If you want to know if the use has ever toggled the button, attach a function to the Checked and Unchecked event and have a Boolean flag in your class to track that.
The button would look something like this (plus your other attributes):
<ToggleButton
Checked="toggleButton_Changed"
Unchecked="toggleButton_Changed" />
In the function body, set the boolean flag to true, meaning the user has toggled your button. If you want to get the current value, check the button's IsChecked property.
There is no built-in property that you can use to determine whether a ToggleButton has even been toggled. The users can toggle between the true, false and null states as many times they want.
You will need to keep track of this yourself. This should be an easy thing to do though. You could for example handle the Checked and Unchecked events as suggested by #pedrolmota and use a field to keep track of whether these event handlers were ever called:
private bool _hasBeenToggled;
private void ToggleButton_Checked(object sender, RoutedEventArgs e)
{
_hasBeenToggled = true;
}
private void ToggleButton_Unchecked(object sender, RoutedEventArgs e)
{
_hasBeenToggled = true;
}
You then simply check the value of the field in your other method:
var ToggleButtonControl = (ToggleButton)ControlName; //
if (!_hasBeenToggled) //<--
{
parameterStr = "/////////////";
//MessageBox.Show("This should pop-up only if the user never toggled the button");
}
else
{
//MessageBox.Show("The user toggle or toggle it back");
if (ToggleButtonControl.IsChecked == false)
parameterStr = "لا";
else
parameterStr = "نعم";
}
If you follow the MVVM design pattern, you could just set the _hasBeenToggled in the setter of your source property.
Related
I have a simple dialog with a SpinEdit and two buttons: OK_Button and Cancel_Button. I've set a mask for the value in the SpinEdit and the dialog won't let me press the cancel button when the value is invalid. I've tried changing the SpinEdit's property to InvalidValueBehavior="AllowLeaveEditor" but then I can click both, OK and cancel button. Is there a way to ONLY allow pressing cancel when the value is incorrect?
XAML:
<dxe:SpinEdit x:Name="dxSpinEdit"
Height="23" MinWidth="200" Width="Auto"
HorizontalAlignment="Right"
Text="{Binding Value, Mode=TwoWay}"
MaskType="Numeric"
IsFloatValue="{Binding FloatValue}"
MinValue="{Binding MinValue}"
MaxValue="{Binding MaxValue}"
Mask="{Binding Mask, Mode=OneWay}"
MaxLength="{Binding Path=InputLength}"
MaskShowPlaceHolders="{Binding ShowPlaceHolder}"
InvalidValueBehavior="WaitForValidValue"
/>
<StackPanel Grid.Row="1" x:Uid="OKCancel_Buttons" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Bottom">
<Button Height="23" x:Name="OK_Button" Click="OK_Click" Content="OK" IsDefault="True" HorizontalAlignment="Right" MinWidth="95" />
<Button Height="23" x:Name="Cancel_Button" Click="Cancel_Click" Content="Cancel" HorizontalAlignment="Right" MinWidth="95" PreviewMouseDown="win_PreviewMouseDown" />
</StackPanel>
I looked up this issue on the devexpress forum but their solution didn't work for me. I've implemented the MouseDownPreview event like so:
C# (code behind)
private void OK_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Close();
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
DialogResult = false;
Close();
}
private void win_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if(e.Source == Cancel_Button)
{
DialogResult = false;
Close();
}
}
But the event wasn't handled at all. I'd like to keep the property InvalidValueBehavior at the value "WaitForValidValue" but at the same time I'd like to allow pressing the Cancel button.
Even if you're not going to go the full MVVM route, you should switch from using click events to an ICommand implementation that supports CanExecute logic (such as this one from MVVM Light).
Using a command will automatically disable any bound control (e.g. button or menu item) when CanExecute is false. You can then have all the logic for controlling your commands grouped in one place, including validation that will only allow OK to be clicked when your object is in a valid state.
If you just want to go the standard WPF (non MVVM) route, you could add something like this in your window's constructor
public MyView()
{
....
Ok_Button.Command =
new RelayCommand(() => DialogResult = true, // just setting DialogResult is sufficient, no need to call Close()
// put the required validation logic here
() => dxSpinEdit.Value > 0 && dxSpinEdit.Value < 10);
Cancel_Button.Command = new RelayCommand(() => DialogResult = false);
// replace this with the actual event from SpinEdit
dxSpinEdit.ValueChanged += (s,e) => (OK_Button.Command as RelayCommand).RaiseCanExecuteChanged();
}
Yes I know it looks ugly 😀 - I'd suggest following the MVVM design pattern instead. When using MVVM, all of the command functionality belongs in your ViewModel.
Either way, you can then remove all the click and mousedown handlers from your buttons.
I am trying to make a WPF listbox replicate the behaviour of an old Winforms CheckedListBox, or the checked list box used in e.g. AnkhSVN. I have seen examples that show how to use a DataTemplate to create a check box for every time (e.g. Wpf CheckedListbox - how to get selected item), but this feels very clunky compared to the winforms control:
The logic of "If the user changes a check state, ensure that check state changes for all selected items" is not present by default.
The hit area to change an item from checked to unchecked is the box /and/ the title, rather than just the box as in Winforms
I can handle the first issue by adding a listener to the PropertyChanged event on each item in the bound collection, and if IsChecked changes, then set IsChecked to the same value for all currently selected items.
However, I cannot find a good solution to the second issue. By splitting the DataTemplate into a Checkbox with no title, and a TextBlock with the title, I can reduce the hit area to change the check state to only the desired square. However, all mouse interaction which hits the TextBlock does nothing - I would like it to behave the same as in a normal listbox, or in the dead space outside of the Textblock: If the user is holding shift, then select everything up to and including this item, if not, then clear the selection and select only this item. I could try to implement something where I handled Mouse* events on the TextBlock, but that seems brittle and inelegant - I'd be trying to recreate the exact behaviour of the ListBox, rather than passing events to the listbox.
Here's what I've got currently:
XAML:
<ListBox x:Name="_lstReceivers" SelectionMode="Extended" Margin="10,41,6,15"
ItemsSource="{Binding Receivers}">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked}" IsHitTestVisible="True"/>
<TextBlock Text="{Binding Item}" Background="{x:Null}" IsHitTestVisible="False"/><!--Attempt to make it pass mouse events through. Doesn't work. Yuk.-->
</StackPanel>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code behind to get the "Change all checks at the same time" logic (removed some error handling for clarity):
private void ListBoxItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var item = sender as CheckableItem<Receiver>;
if (item == null)
return;
if (e.PropertyName == nameof(CheckableItem<Receiver>.IsChecked))
{
bool newVal = item.IsChecked;
foreach (CheckableItem<Receiver> changeItem in _lstReceivers.SelectedItems)
{
changeItem.IsChecked = newVal;
}
}
}
By trying various combinations of Background = "{x:Null}" and IsHitTestVisible="False", I did manage to get the entire item to not respond to mouse click events - but I could not make it have only the Checkbox respond to mouse events, while everything else is passed to the ListBox for proper selection processing.
Any help would be greatly appreciated.
Answering my own question again.
Well, I couldn't find a clean way to do it, so I ended up setting the ListBoxItem to have IsHitTestVisible="False", and manually tracing mouse events using PreviewMouseDown.
Final code:
XAML:
<ListBox x:Name="_lstReceivers" SelectionMode="Extended" Margin="10,41,6,15"
ItemsSource="{Binding Receivers}" PreviewMouseDown="_lstReceivers_MouseDown">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem IsSelected="{Binding IsSelected}" IsHitTestVisible="False">
<StackPanel Orientation="Horizontal" Background="{x:Null}">
<CheckBox IsChecked="{Binding IsChecked}" IsHitTestVisible="True" Checked="CheckBox_Checked" Unchecked="CheckBox_Checked"/>
<TextBlock Text="{Binding Item}" Background="{x:Null}" IsHitTestVisible="False"/>
</StackPanel>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code behind:
//Logic to handle allowing the user to click the checkbox, but have everywhere else respond to normal listbox logic.
private void _lstReceivers_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Visual curControl = _lstReceivers as Visual;
ListBoxItem testItem = null;
//Allow normal selection logic to take place if the user is holding shift or ctrl
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl) || Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
return;
//Find the control which the user clicked on. We require the relevant ListBoxItem too, so we can't use VisualTreeHelper.HitTest (Or it wouldn't be much use)
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(curControl); i++)
{
var testControl = (Visual)VisualTreeHelper.GetChild(curControl, i);
var rect = VisualTreeHelper.GetDescendantBounds(testControl);
var pos = e.GetPosition((IInputElement)curControl) - VisualTreeHelper.GetOffset(testControl);
if (!rect.Contains(pos))
continue;
else
{
//There are multiple ListBoxItems in the tree we walk. Only take the first - and use it to remember the IsSelected property.
if (testItem == null && testControl is ListBoxItem)
testItem = testControl as ListBoxItem;
//If we hit a checkbox, handle it here
if (testControl is CheckBox)
{
//If the user has hit the checkbox of an unselected item, then only change the item they have hit.
if (!testItem.IsSelected)
dontChangeChecks++;
((CheckBox)testControl).IsChecked = !((CheckBox)testControl).IsChecked;
//If the user has hit the checkbox of a selected item, ensure that the entire selection is maintained (prevent normal selection logic).
if (testItem.IsSelected)
e.Handled = true;
else
dontChangeChecks--;
return;
}
//Like recursion, but cheaper:
curControl = testControl;
i = -1;
}
}
}
//Guard variable
int dontChangeChecks = 0;
//Logic to have all selected listbox items change at the same time
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
if (dontChangeChecks > 0)
return;
var newVal = ((CheckBox)sender).IsChecked;
dontChangeChecks++;
try
{
//This could be improved by making it more generic.
foreach (CheckableItem<Receiver> item in _lstReceivers.SelectedItems)
{
item.IsChecked = newVal.Value;
}
}
finally
{
dontChangeChecks--;
}
}
This solution works, but I don't like the coupling it introduces between my code and the exact behaviour of the ListBox implementation:
Checking the Keyboard state
It won't handle dragging if the user starts dragging inside a checkbox
It should happen on mouseup, not mousedown. But it's close enough for my needs.
PS: The bound class, even though it's irrelevant and obvious what it would have:
public class CheckableItem<T> : INotifyPropertyChanged
{
public T Item { get; set; }
private bool _isSelected;
public bool IsSelected
{
get => _isSelected;
set
{
if (_isSelected == value)
return;
_isSelected = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSelected)));
}
}
private bool _checked;
public bool IsChecked
{
get => _checked;
set
{
if (_checked == value)
return;
_checked = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsChecked)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
I am new in coding and atm trying to understand events what is an annoying stage but super important I guess. I just made a tic tac toe game and it is working but not really "beautiful" coded. I really have problems in using the events. well I am reading 3 different books, google is my best friend and I guess I red all the StackOverflow posts about events but the bulb in my head is never shining :P so I will give you boys a part of my code and I added some comments for the understanding:
/*I have 9 buttons(3x3) which are the play field */
private void Feld1_Click(object sender, RoutedEventArgs e)
{
// in my game each player have 1 Radiobutton so they check a RButton and then it's their turn
if (Player1RButton.IsChecked == true)
{
// i dont wanted to use "X" or "O" so i chose the colors green and yellow
Feld1.Background = Brushes.Green;
// Feld1G is for example that Player1 (green) is "owning" this
// field/button so i can later check who won the game
Feld1G = 1;
Feld1Y = 0;
}
if (Player2RButton.IsChecked == true)
{
//here is the same thing happening like in the example of green
Feld1.Background = Brushes.Yellow;
Feld1Y = 1;
Feld1G = 0;
}
}
private void Feld2_Click(object sender, RoutedEventArgs e)
{
if (Player1RButton.IsChecked == true)
{
Feld2.Background = Brushes.Green;
Feld2G = 1;
Feld2Y = 0;
}
if (Player2RButton.IsChecked == true)
{
Feld2.Background = Brushes.Yellow;
Feld2Y = 1;
Feld2G = 0;
}
}
here is an example how the ui looks like:tic tac toe exampe
now what I would like to do in my words cause I don't know how to code it:
// I have no idea if this is the right start
public void OnClick (EventArgs e)
{
/* now I guess here have to happen something like this, for example, field9 was clicked and radiobutton2 is checked: know that button9 have been clicked know radiobutton is checked and now brush (this.button?) button/field9 and set Feld9Y=1;
}
*/
I want to make it a bit more clearly here: I want all the functions run from the method above and not in each button event for itself
so my questions:
1. what do I have to do to make this work the way i explained above to make 1 method for all of my buttons
and it would be great if you boys could make a good story why I have to use it this way and how it works so a brainless ape like me can understand the event stuff and the bulb will finally shine bright like a diamond :P
Edit:here is the link for my whole code:
https://codereview.stackexchange.com/questions/164462/c-events-in-a-tic-tac-toe-game
Here is a quick example to get you started. I only implemented changing the background and nothing more.
Below is the xaml. I set up a very simple board with 9 regular buttons and 2 radio buttons. I did not implement anything else.
<Grid x:Name="board">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Content="Button"/>
<Button Content="Button"
Grid.Column="1" />
<Button Content="Button"
Grid.Column="2"/>
<Button Content="Button"
Grid.Row="1" />
<Button Content="Button"
Grid.Row="1"
Grid.Column="1" />
<Button Content="Button"
Grid.Row="1"
Grid.Column="2" />
<Button Content="Button"
Grid.Row="2" />
<Button Content="Button"
Grid.Row="2"
Grid.Column="1" />
<Button Content="Button"
Grid.Row="2"
Grid.Column="2" />
<RadioButton x:Name="playerOneRadioButton"
IsChecked="True"
Content="Player 1"
Grid.Column="3"
HorizontalAlignment="Left"
Margin="10,10,0,0"
Grid.Row="1"
VerticalAlignment="Top" />
<RadioButton x:Name="playerTwoRadioButton"
Content="Player 2"
Grid.Column="3"
HorizontalAlignment="Left"
Margin="10,30,0,0"
Grid.Row="1"
VerticalAlignment="Top" />
</Grid>
Below is the code behind.
I hooked up each buttons click event to point to the Button_Click method.
In that method I first cast the sender to type Button. I then change the background color of the button to either yellow or green.
public partial class MainWindow: Window
{
public MainWindow( )
{
InitializeComponent( );
// Iterate through all of the child elements
// contained in the Grid control which I named "board".
foreach( var control in board.Children.OfType<Button>( ) )
{
// Hook up the event handler of each button.
control.Click += Button_Click;
}
}
private void Button_Click( object sender, RoutedEventArgs e )
{
// Safe cast the sender to type Button.
var button = sender as Button;
if( button == null ) return;
// Change the background color of the button
// yellow or green based on which radio button
// is checked.
button.Background = playerOneRadioButton.IsChecked.Value ? Brushes.Green : Brushes.Yellow;
}
}
EDIT:
Question 1: The Grid which I have on the design surface is named "board". Children is a property of the Grid control. All child elements (elements that reside inside of the grid, like the buttons) are added to the Children property of the grid control. The Children property is a collection, or you could say a list, of all child elements that reside in the grid control. I loop through each child control in the grid that is of type button.
Question 2: The ?: is called a ternary operator. It is used for writing a conditional statement (an if statement). You can also write it as follows:
if( playerOneRadioButton.IsChecked.Value )
{
button.Background = Brushes.Green;
}
else
{
button.Background = Brushes.Yellow;
}
Question 3: I'm currently casting the object sender to type button using whats called a "safe cast". In other words, if the cast fails (say sender wasn't a button and was some other control instead) then null is returned. I check for this null condition to ensure that the cast was successful. If the button variable is null then the cast was not successful and I want to exit (return) out of that method. No code below the
if(button == null) return;
will execute.
The single method you want to use should look like this:
private void Feld_Click(object sender, RoutedEventArgs e)
{
var currentButton = (Button)sender;
if (Player1RButton.IsChecked == true)
{
currentButton.Background = Brushes.Green;
}
}
Add this as OnClick handler for all the buttons.
From there you can calculate the Feld color whenever you need it, by accessing the Background property in a separate method.
i have a 100 * 100 grid on one of the page in my app. i have a single checkbox on the top right of the grid.
what i want is that when the user tap or click on the grid first time the checkbox become checked and when i again taps on the grid the checkbox becomes unchecked. how can i achieve it? i am talking about the metro type checkboxes here. or could i use checkbox itself in this manner? is grid the right way to go?
In short i need some guidelines in solving the above problem?
This may work for you
<Grid Height="100" Width="100" Background="Beige" Tap="Grid_Tap" >
</Grid>
<CheckBox x:Name="gridCheckBox" Content="CheckBox" HorizontalAlignment="Left" Margin="369,47,0,0" VerticalAlignment="Top"/>
then for making the checkbox checked and unchecked on the tap event of the grid we need to add the following code
private void Grid_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
if (gridCheckBox.IsChecked == true)
{
gridCheckBox.IsChecked = false;
}
else
{
gridCheckBox.IsChecked = true;
}
}
gridcheckbox.IsChecked = (gridcheckbox.IsChecked == true) ? false : true;
I am trying to implement localisation in my wp8 app.
I have two radio buttons (Arabic and English) when a user selects one of the radio button then the pop up appears that is he sure he want to change language of the app if he chooses ok then the language of the app changes but if he chooses cancel then there will be no change in the language but the issue is that the radio button toggles even if user presses cancel.
How to stop the toggle to happen ?
Here is my Xaml code and the code which is binded if any radio button is checked ..
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel Orientation="Vertical" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="20">
<RadioButton Content="English" Foreground="Black" FontSize="30" IsChecked="{Binding isenglishchecked,Mode=TwoWay, Source={StaticResource appSettings}}" Checked="RadioButton_Checked"/>
<RadioButton Content="Arabic" Foreground="Black" FontSize="30" IsChecked="{Binding isarabicchecked,Mode=TwoWay, Source={StaticResource appSettings}}" Checked="RadioButton_Checked"/>
</StackPanel>
</Grid>
public bool isenglishchecked
{
get
{
return GetValueOrDefault<bool>(isenglishcheckedname, isenglishcheckedDefault);
}
set
{
var result = MessageBox.Show("This Will Change the Language of the App Do you Want to continue ?", "Language Change", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
{
if (AddOrUpdateValue(isenglishcheckedname, value))
{
Save();
if (isenglishchecked)
{
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
MainViewModel.stationnamelist = null;
Messenger.Default.Send<string>("mainpage", "tomainpage");
(Application.Current.RootVisual as PhoneApplicationFrame).Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
}
else
{
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("ar-AE");
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("ar-AE");
MainViewModel.stationnamelist = null;
Messenger.Default.Send<string>("mainpage", "tomainpage");
(Application.Current.RootVisual as PhoneApplicationFrame).Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
}
}
}
else
{
(Application.Current.RootVisual as PhoneApplicationFrame).Navigate(new Uri("/Skins/SelectLanguage.xaml", UriKind.Relative));
}
}
}
public bool isarabicchecked
{
get
{
return GetValueOrDefault<bool>(isarabiccheckedname, isarabiccheckedDefault);
}
set
{
if (AddOrUpdateValue(isarabiccheckedname, value))
{
Save();
}
}
}
What's likely happening is that the UI layer (Silverlight), in response to the user's tap, happily changes its own state, and then sets the value of isenglishchecked or isarabicchecked. The fact that the value of the property doesn't change is of no consequence -- Silverlight doesn't notice. (not changing the value of the property in response to a set call is non-standard behavior, so it's not surprising that Silverlight would "misbehave" like this)
There are a few things you can try:
In the set handler for both properties, raise the PropertyChanged event (in INotifyPropertyChanged) for isenglishchecked and isarabicchecked. This will cause the UI to re-query the view model so it's back in sync with it. If the checked properties didn't change, the UI will notice this.
Use the built-in ExceptionValidationRule on each RadioButton, then throw an exception in the Set handler if the user cancels the operation. This might cause the radio button's value not to change (good), but it might also make your button red.
Define your own ValidationRule. It might work better to show the message box during ValidationRule.Validate.
Ditch the confirmation dialog entirely and just change the language immediately. If it was a mistake, the user can just tap the other button.