Is there a BeforeCheck for WPF checkboxes? - c#

Some of the checkboxes on my form should not be able to be checked/unchecked by users. Is there a way for me to cancel the event before the checbox's Check event is triggered?
in winForms it was easy, just
public void cb_BeforeChecked(object sender, EventArgs e){
e.Handled = true;
}
but I cannot find anything like this in WPF...I figure you can probably do it, just need to do something fancy..
Thanks!

Why not just set IsEnabled="False"?

You can set IsHitTestVisible="False" to make it not respond to user clicks. Otherwise you can bind it to a command if viewmodel logic determines whether it is clickable.
<Grid>
<CheckBox IsHitTestVisible="False" Content="I cannot be clicked at all"/>
<CheckBox Command="{Binding DoSomethingCommand}" Content="I can be clicked if DoSomethingCanExecute returns true."/>
</Grid>
In your DataContext (Viewmodel or otherwise):
RelayCommand _DoSomethingCommand = null;
public ICommand DoSomethingCommand
{
get
{
if (_DoSomethingCommand== null)
{
_DoSomethingCommand= new RelayCommand(
param => DoSomething(),
param => DoSomethingCanExecute
);
}
return _DoSomethingCommand;
}
}
public bool DoSomethingCanExecute
{
get
{
return CheckboxShouldBeEnabled();
}
}
public void DoSomething()
{
//Checkbox has been clicked
}

This might be a bit of an overkill, but you could sub-class CheckBox and then override the OnClick() method.

Only setting IsHitTestVisible="False" just takes care of the Mouse, the users can still use the KeyBoard to tab to the CheckBox and change the value.
You should set both IsHitTestVisible="False" and Focusable="False" to disable the KeyBoard as well

You can have the check box disabled, and associate a style with the disabled check box, if the disabled look is a problem. As its already pointed in the previous posts, its good to have different looks for different states.

Related

WPF Focus after Command - Why does it land on my Window?

I am trying to make a small modal window with a search box and a list of results.
My goal is for the focus to land in the first element of the List after searching.
Problem
Unfortunately, I stuck in a situation where my focus lands on the window itself after searching.
My TextBox has an InputBinding that triggers on enter to perform the search:
<TextBox Grid.Column="1" Grid.Row="1"
Text="{Binding Supplier.LeverandorNavn, UpdateSourceTrigger=PropertyChanged}"
x:Name="txtBoxSupplierName" Width="150"
HorizontalAlignment="Left" VerticalAlignment="Top"
TabIndex="1">
<TextBox.InputBindings>
<KeyBinding Key="Return" Command="{Binding SearchCommand}" />
</TextBox.InputBindings>
</TextBox>
I have been trying to use Snoop to understand the flow of events.
As seen here, the KeyDown is handled by my txtBoxSupplerName Textbox fine.
But I cannot understand why the levWindow gets focus after my command executes.
I have attempted to manually set the focus element, but it has no effect.
Questions
Can someone explain to me, why the focus lands on the window by default?
Can someone suggest an approach, on how I can take control of the focus myself and avoid this default behavior?
I am challenged by the fact that the DataGrid needs some time to redraw itself before the row I want to focus is visible.
Properties
public ClientLeverandor ValgtLeverandør
{
get { return _valgtLeverandør; }
set { SetProperty(ref _valgtLeverandør, value, nameof(ValgtLeverandør)); }
}
public ObservableCollection<ClientLeverandor> Leverandører
{
get { return _leverandører; }
private set { SetProperty(ref _leverandører, value, nameof(Leverandører)); }
}
public ListCollectionView LeverandørView
{
get { return _leverandørView; }
set { SetProperty(ref _leverandørView, value, nameof(LeverandørView)); }
}
Command Implementation
using (BusyIndicator.ShowInScope(Strings.HenterData_BusyText))
{
var leverandører = await SøgLeverandører(Supplier);
if (leverandører.Any())
{
Leverandører.Clear();
foreach (var lev in leverandører) Leverandører.Add(lev);
ValgtLeverandør = Leverandører[1];
SuppliersAdded?.Invoke();
}
}
I was unable to find an explanation for why KeyboardFocus land on my window or why my attempts to set the focus did not have effect.
But i was finally able to find a solution, where my attempt worked.
I used a Dispatcher with BackgroundPriority to invoke my method to set focus after whatever was setting focus to the window. This worked for both Keyboard and Mouse input.
private void ViewModel_SuppliersAdded()
{
Dispatcher.BeginInvoke(new Action(SetFocusToFirstRow), DispatcherPriority.Background);
}

WPF - switching between two way and one way bindings?

In my 'View' I have a TextBox bound to a ViewModel's string property.
I want to add a submit button to the View, so the underlying ViewModels string property is only updated when this is pressed.
To further complicate things, this TextBox is inside a DataGrid. I think setting the bindings UpdateSourceTrigger to Explicit may be the answer but I can't see how this would work.
Any alternative solution would be to switch the ViewModels String with a TextBox - meaning I would manually populate data.
You can bind button to command and pass text of textbox as parameter.
<TextBox x:Name="textBox"></TextBox>
<Button Content="Button" Command="{Binding MyCommand}" CommandParameter="{Binding ElementName=textBox, Path=Text}"/>
In your ViewModel:
public ICommand MyCommand
{
get
{
return new RelayCommand((textBoxText) =>
{
if (...)
{
//somelogic;
}
});
}
}
In the Button you access the row via the DataContext
private void ButtonRevise_Click(object sender, RoutedEventArgs e)
{
Button btn = (Button)sender;
GabeLib.SearchItem srchItem = (GabeLib.SearchItem)btn.DataContext;

Determine the selected HubSection in a Hub with two HubSections

I have a hub control with two HubSections. When selected HubSection changes, I want to change the contents of the AppBar with section specific buttons.
Listening SectionsInViewChanged event is the general solution recommended to implement this behavior but this event is not fired when there are only two HubSections.
Is there another event that can be used to determine the current HubSection?
Thanks.
#Depechie has pointed you in the right direction.. You can use the SelectionHub control I created and add an event to it that fires when the selected index changes
public event EventHandler SelectionChanged;
private static void OnSelectedIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var hub = d as SelectionHub;
if (hub == null) return;
// do not try to set the section when the user is swiping
if (hub._settingIndex) return;
// No sections?!?
if (hub.Sections.Count == 0) return;
hub.OnSelectionChanged();
}
private void OnSelectionChanged()
{
var section = Sections[SelectedIndex];
ScrollToSection(section);
var handler = SelectionChanged;
if(handler != null)
{
handler(this, EventArgs.Empty);
}
}
You can extend this by adding selection changed event args to it. With this you can then subscribe to the event in your code behind and change the app bar buttons based on the index.
After reading Shawn's article suggested by #Depechie. I tried to implement the same solution in my app in order to update contents of the AppBar with section specific buttons.
Despite my efforts I was unable to make it work so I modified some parts of the solution. I used the behavior solution and changed only the ScrollerOnViewChangedfunction as follows. This might not be the best way or may cause unexpected results in different scenarios but in my case it worked without a problem.
private void ScrollerOnViewChanged(object sender, ScrollViewerViewChangedEventArgs scrollViewerViewChangedEventArgs)
{
_settingIndex = true;
ScrollViewer scrollViewer = sender as ScrollViewer;
if (scrollViewer.HorizontalOffset > (scrollViewer.ViewportWidth / 2))
SelectedIndex = 1;
else
SelectedIndex = 0;
_settingIndex = false;
}
After that I added a property to my viewmodel in order to store selected index.
private int _selectedIndex;
public int SelectedIndex
{
get { return _selectedIndex; }
set
{
SetProperty(ref this._selectedIndex, value);
}
}
I used the behavior in the XAML in order to update SelectedIndex in my ViewModel.
<Hub>
<i:Interaction.Behaviors>
<behaviors:HubSelectionBehavior SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}" />
</i:Interaction.Behaviors>
<HubSection>...</HubSection>
<HubSection>...</HubSection>
</Hub>
The last thing to do was to set the visibility of AppBarButtons using this property. SectionIndexToVisibilityConverter compares SelectedIndex to ConverterParameter and returns Visibility.Visible if they are equal.
<CommandBar>
<AppBarButton Label="Open" Icon="World" Command="{Binding OpenInBrowserCommand}" Visibility="{Binding SelectedIndex, Converter={StaticResource SectionIndexToVisibilityConverter}, ConverterParameter=0}"/>
<AppBarButton Label="Previous" Icon="Back" Command="{Binding PreviousAnswerCommand}" Visibility="{Binding SelectedIndex, Converter={StaticResource SectionIndexToVisibilityConverter}, ConverterParameter=1}"/>
<AppBarButton Label="Next" Icon="Forward" Command="{Binding NextAnswerCommand}" Visibility="{Binding SelectedIndex, Converter={StaticResource SectionIndexToVisibilityConverter}, ConverterParameter=1}"/>
</CommandBar>
Thanks #Depechie for suggesting the article and #Shawn for writing the article : )

Why would my event handler not get called?

I've got this xaml:
<Button VerticalAlignment="Bottom"
Click="SaveAndGoBack"
IsEnabled="{Binding Frame.CanGoBack,
ElementName=pageRoot}"
Style="{StaticResource BackButtonStyle}" />
...but on "tapping" (clicking) the back button, while it does return to the previous page, SaveAndGoBack() is not called - I have a breakpoint on the first line, and it is not reached.
The button is obviously enabled, because I am able to select it and "go back." So why would the event handler not be reached?
UPDATE
Changing clicked to tapped makes no difference. This is the XAML now:
<Button VerticalAlignment="Bottom"
Tapped="SaveAndGoBack"
IsEnabled="{Binding Frame.CanGoBack,
ElementName=pageRoot}"
Style="{StaticResource BackButtonStyle}" />
...and this is the method, with a breakpoint on the first line, which is never reached (but the Main Page is returned to):
private async void SaveAndGoBack(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs args)
{
SOs_Locations sos_locs = new SOs_Locations { GroupName = txtbxGroup.Text };
int locNum;
int.TryParse(txtbxLocationNum.Text, out locNum);
sos_locs.LocationTitle = txtbxTitle.Text;
sos_locs.CivicAddress = txtbxAddress.Text;
double lat;
double.TryParse(txtbxLatitude.Text, out lat);
sos_locs.Latitude = lat;
double lng;
double.TryParse(txtbxLongitude.Text, out lng);
sos_locs.Longitude = lng;
if (cmbxDisplayColor.SelectedValue != null)
sos_locs.LocationColor = cmbxDisplayColor.SelectedValue.ToString();
await SQLiteUtils.UpdateLocationAsync(sos_locs);
Frame.Navigate(typeof(MainPage));
}
Click is a different event from a Tap. you should look for an event called Tapped and create an OnTapped handler for it.
My guess is that for your issue. The event isn't bubbling because in the LayoutAwarePage.cs the event is set to handled.
You can modify this page to suit your needs. Look for the CoreWindow_PointerPressed method and look for the area where it sets args.Handled = true
Use the Tapped event instead as it handles click, tap and stylus events whereas Click deals specifically with the mouse (see this answer for more details).
Here's some information on how they've tried to unify the events around the different pointing input modalities around touch.

Disable ComboBox Without Changing Appearance

Is there a way to disable changing the value of a ComboBox in WPF without giving it the visual properties of a disabled ComboBox? For example, I know that for a text field you can set the IsReadOnly property to true. Doing this for a ComboBox however, does not prevent the user from selecting a different value.
Mr. Benages, I think setting IsHitTestVisible and Focusable to false on the ComboBox might do the trick. Hope this helps.
While I agree that a disabled control should look disabled you could just set the ComboBox ControlTemplate to the standard one (or one your using) removing any of the standard functionality
eg This will give you a decent looking readonly combobox
<ComboBox>
<ComboBox.Template>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<Microsoft_Windows_Themes:ListBoxChrome x:Name="Border" Height="23" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" RenderMouseOver="{TemplateBinding IsMouseOver}"/>
<TextBlock FontSize="{TemplateBinding FontSize}" VerticalAlignment="Center" Text="Selected Item" Margin="5,0,0,0"></TextBlock>
</Grid>
</ControlTemplate>
</ComboBox.Template>
</ComboBox>
you'll need to include the following namespace
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
Are you sure this is a good idea from a usability/conventions standpoint? If your goal is readability, perhaps you can change the disabled color to bump up the contrast a bit.
http://social.msdn.microsoft.com/forums/en-US/wpf/thread/45dd7614-326b-4a51-b809-d25a3ff1ade8/
Anyway, I suspect you could write an onChange event handler to reset the value to the previous entry.
Im not sure if its the same in .net however back in the VB6 days i use to get a picture box, frame or other container (sorry off the top of my head i can't remember which). I would put the combobox within that. To the users it looks the same. When you disable the container this would lock out the combo box as well, leaving it looking normal.
You can set the Foreground and Background colors and that seems to override the disabled colors. The drop down button shows as disabled which is good.
EDIT My code I tested with in IE 6/Kaxaml.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<ComboBox Foreground="black" Background="white" IsEditable="True" Text="Hello" IsEnabled="false">
<ComboBoxItem>Test1</ComboBoxItem>
<ComboBoxItem>Test2</ComboBoxItem>
<ComboBoxItem>Test3</ComboBoxItem>
</ComboBox>
</StackPanel>
</Page>
Why then, use a comboBox.
My choice would be a label within a border that takes the place of the control, indicating that this is a display-only screen.
If it must look like a combobox, it would be better to use an object themed like a button but not clickable. You could even draw a gray dropdown arrow so it better looks like a comboBox.
It just seems overkill to actually have a combobox on the screen that people can't interact with, when a label would do fine.
...prevent the user from selecting a different value.
On top of styling you can disable keyboard input by overriding some ComboBox methods:
using System.Windows.Controls;
using System.Windows.Forms;
using System.Windows.Input;
public class LockableComboBox : ComboBox
{
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
if (this.IsReadOnly)
{
e.Handled = true;
}
else
{
base.OnSelectionChanged(e);
}
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
if (this.IsReadOnly)
{
if ((e.Key == Key.C || e.Key == Key.Insert)
&& (Keyboard.Modifiers & ModifierKeys.Control)
== ModifierKeys.Control)
{
// Allow copy
Clipboard.SetDataObject(SelectedValue, true);
}
e.Handled = true;
}
else
{
base.OnPreviewKeyDown(e);
}
}
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
if (this.IsReadOnly)
{
e.Handled = true;
}
else
{
base.OnPreviewTextInput(e);
}
}
protected override void OnKeyUp(KeyEventArgs e)
{
if (this.IsReadOnly)
{
e.Handled = true;
}
else
{
base.OnKeyUp(e);
}
}
}

Categories

Resources