UWP CustomRenderer for Checkbox: Pointer over Checkbox changes style? - c#

I'm working with Xamarin.Forms and I made a CustomRenderer for Checkbox in UWP. When I set all the Checkboxes of my items in the ListView to true by clicking the button "Alle", the Checkboxes are displayed correctly with the check inside the box:
However, if I hover my mouse over the Checkboxes, they immediately change their appearence (the check disappears but it's still selected). In the following screenshot, I moved my cursor over the 3rd - 7th Checkboxes:
This is my overridden OnElementChanged method in the CustomRenderer:
protected override void OnElementChanged(ElementChangedEventArgs<EvaCheckbox> e)
{
base.OnElementChanged(e);
var model = e.NewElement;
if (model == null)
{
return;
}
nativeCheckbox = new CheckBox();
CheckboxPropertyChanged(model, null);
model.PropertyChanged += OnElementPropertyChanged;
nativeCheckbox.Checked += (object sender, Windows.UI.Xaml.RoutedEventArgs eargs) =>
{
model.IsChecked = (bool)nativeCheckbox.IsChecked;
};
nativeCheckbox.Unchecked += (object sender, Windows.UI.Xaml.RoutedEventArgs eargs) =>
{
model.IsChecked = (bool)nativeCheckbox.IsChecked;
};
SetNativeControl(nativeCheckbox);
}
I tried to override the PointerEntered event of nativeCheckbox. It works, for example if I set the model.IsChecked to true on this event, it will be set to true:
nativeCheckbox.PointerEntered += (s, args) =>
{
model.IsChecked = true;
};
But I don't know how to (if even at this place) prevent the checkbox from changing it's appearance when moving the cursor above the Checkbox. Just leaving the triggered event with empty code like this won't change anything about the described behaviour:
nativeCheckbox.PointerEntered += (s, args) => { };
How can I prevent the Checkbox from changing it's appearance when I move my cursor over it?
Update:
I've created a sample project for this issue. You can find the repository here: https://github.com/Zure1/CustomCheckbox
It has the exact same described behavior. In the following screenshot I pressed the button "All" on the bottom of the screen and then the checkboxes look like correct with a check inside of them:
After moving the mouse cursor over the bottom 3 checkboxes, their change their appearance:
Information: I'm debugging on my desktop (Windows 10). I don't know if this issue exists on WinPhone. Just in case you're wondering why my checkboxes are red: My system color in Windows is red.

This is a tricky one as I have been struggling with this issue for a while, I'll try my best to answer this.
TL;DR: It's caused by ViewCell.
The issue comes down to Xamarin Forms ListView and ViewCell.
I haven't been able to track down the cause yet for many months and the way I get around this issue is by refreshing the ListView every time a change happens forcing a redraw of the entire ListView which can really impact performance.
My educated guess on what the cause could be is the rendering code for the ViewCell is missing something.
As for your particular issue, I have created a CheckBoxCell which you can use to display a list of checkboxes with a title. I forked your project and made the changes.
This will display something similar to what you are trying to achieve and doesn't have rendering issues so will be a good starting point. You are able to customize this to display images and the like but you'll have to do that in the platform-specific layout code.
Please note that I have only created the code for UWP and that should be enough to get you going for the other platforms.
I hope this helps somewhat.

Related

GridView doesn't scroll down to virtualized items

In my application, I need to select the newly created document(note) when I go back to library. After library item is selected, the Library must be scrolled to the selected item.
My library's OnLoaded method:
private async void OnLoaded(object sender, RoutedEventArgs e)
{
await this.ViewModel.InitializeAsync();
// CollectionViewSource of my GridView being filled
ViewModel.CollectionChanging = true;
GroupInfoCVS.Source = ViewModel.GroupsCollection;
ViewModel.CollectionChanging = false;
// Loading Last selected item - THIS CHANGES SELECTION
ViewModel.LoadLastSelection();
}
After I call the LoadLastSelection method, selection is changed successfuly (I've tested). This is the method that is called after that (in our GridView's extended control):
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.SelectedItemsCount = this.SelectedItems.Count;
var newlySelectedItems = e.AddedItems;
if (newlySelectedItems.Any())
{
var item = newlySelectedItems.Last();
ScrollTo(item);
}
}
private void ScrollTo(object item)
{
UpdateLayout();
var itemUI = (FrameworkElement)this.ContainerFromItem(item);
if (itemUI != null)
{
_scrollViewer.UpdateLayout();
_scrollViewer.ChangeView(null, itemUI.ActualOffset.Y - itemUI.ActualHeight, null, false);
}
}
This also works for the most part. When itemUI is not null, the method scrolls successfully to the required item. The problems start when the items start to overflow the screen size. When items are completely hidden from the screen, they are virtualized. That means that ContainerFromItem returns null, so I can't take the offset properties. Keep in mind that this actually occurs before Library's OnLoaded method is finished.
Please, help me with some alternative to get such properties or other methods of scrolling, which will help me scroll successfully.
I've read a lot and tried using Dispatcher.RunAsync and ScrollIntoView methods, but I couldn't manage to produce any scrolling behavior. If you point me how to use them successfully, that would be a nice help too.
Here's what I've read (and tried):
ItemContainerGenerator.ContainerFromItem() returns null?
How to Know When a FrameworkElement Has Been Totally Rendered?
Is there a "All Children loaded" event in WPF
Let ListView scroll to selected item
Thanks in advance!
IMPORTANT: If you don't want to read all the conversation within the official answer, please read the solution in short here:
TemplatedControl's style had changed ScrollViewer's name from "ScrollViewer" to "LibraryScrollViewer" and that rendered ScrollIntoView method useless.
For GridView, the best way to achieve your needs is to call GridView.ScrollIntoView.
But you seem to have made similar attempts, and it does not to be successful, then the following points may help you:
1. Don't use GridView as a child element of ScrollViewer.
In your code, I see that you are calling the method of ScrollViewer.ChangeView to adjust the view scrolling, so it is speculated that you may put the GridView in the ScrollViewer, which is not recommended.
Because there is a ScrollViewer inside the GridView, and its ScrollIntoView method is to change the scroll area of the internal ScrollViewer. When there is a ScrollViewer outside, the ScrollViewer inside the GridView will lose the scrolling ability, thus making the ScrollIntoView method invalid.
2. Implement the Equals method of the data class.
If your data class is not a simple type (such as String, Int32, etc.), then implementing the Equals method of the data class will help the GridView to find the corresponding item.
Thanks.

Second TextBox showing same Text Selection as first

Long time listener, first time caller here. I'm having a strange issue with the TextBox in WinRT C#/XAML that I hope someone may be able to help me with.
Basically, I'm working on creating a Custom Control that essentially requires a second TextBox to be a copy of the first, including showing the same Text, and showing the same Selected Text. Obviously for the Text requirement I simply respond to the TextChanged event on the first TextBox and set the Text of the second TextBox to the Text from the first, which works great.
For the Selected Text requirement I started with a similar solution, and my code for this is as follows:
void TextBox1_SelectionChanged(object sender, RoutedEventArgs e)
{
this.TextBox2.Select(this.TextBox1.SelectionStart, this.TextBox1.SelectionLength);
}
This seemed to work pretty well when initially used with a mouse:
But I'm having a problem when selecting text with Touch. I double-tap within the TextBox to create the first "anchor" as you do in Touch, then drag to begin the selection; but I only ever manage to select a single character normally before the selection stops. The TextBox doesn't lose focus exactly, but the behaviour is similar to that; the selection anchors disappear and I can't continue selecting anything unless I re-double-tap to start a new selection. If I remove the code to select text in TextBox2 then the Touch selection behaves perfectly in TextBox1.
I've been trying to fix this for a while and cannot, I'm not sure if I can get the desired behaviour with WinRT TextBoxes. Does anyone have any ideas? Or perhaps another way to implement a solution with two TextBoxes with this behaviour?
Thanks a lot.
So this is far from an answer, but discovered a few things that maybe will help you or others come up with a potential workaround. Apologies if these are things you've already seen and noted.
First, it's not the call to TextBox2.Select() that's the problem per se. This for instance, works fine for me
private void txt1_SelectionChanged(object sender, RoutedEventArgs e)
{
var start = TextBox1.SelectionStart;
var length = TextBox1.SelectionLength;
TextBox2.Select(3, 5);
}
unfortunately, using start and length versus the hard-coded 3 and 5, that is, the following, DOES NOT WORK:
private void txt1_SelectionChanged(object sender, RoutedEventArgs e)
{
var start = TextBox1.SelectionStart;
var length = TextBox1.SelectionLength;
TextBox2.Select(start, length);
}
I also discovered that I could select TWO characters if I started from the end, but only one from the beginning. That got me to thinking about dispatching the call to set the second selection:
private void txt1_SelectionChanged(object sender, RoutedEventArgs e)
{
var start = TextBox1.SelectionStart;
var length = TextBox1.SelectionLength;
Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Low,
() => TextBox2.Select(start, length));
}
Now I can select 2 from the front and 3 and sometimes 4 from the back. Took it a step further, and was able to select as many as six or seven with a really fast swipe.
private void txt1_SelectionChanged(object sender, RoutedEventArgs e)
{
var start = TextBox1.SelectionStart;
var length = TextBox1.SelectionLength;
Dispatcher.RunIdleAsync((v) => Highlight());
}
public void Highlight()
{
TextBox2.Select(TextBox1.SelectionStart, TextBox1.SelectionLength);
}
Seems like the trick to working around this is not setting TextBox2 until whatever vestiges of the TextBox1 SelectionChanged event have completed.
This may be worth registering on Connect.
Mine is only a partial solution as well.
I did some debugging and noticed that the SelectionChanged event is fired throughout the text selection process. In other words, a single finger "swipe" will generate multiple SelectionChanged events.
As you found out, calling TextBox.Select during a text selection gesture affects the gesture itself. Windows seems to stop the gesture after the programmatic text selection.
My workaround is to delay as long as possible calling the TextBox.Select method. This does work well, except for one edge case. Where this method fails is in the following scenario:
The user begins a select gesture, say selecting x characters. The user, without taking their finger off the screen, pauses for a second or two. The user then attempts to select more characters.
My solution does not handle the last bit in the above paragraph. The touch selection after the pause does not actually select anything because my code will have called the TextBox.Select method.
Here is the actual code. As I mentioned above, there are multiple selection changed events fired during a single selection gesture. My code uses a timer along with a counter to only do the programmatic selection when there are no longer any pending touch generated selection changed events.
int _selectCounter = 0;
const int SELECT_TIMER_LENGTH = 500;
async private void TextBox1_SelectionChanged(object sender, RoutedEventArgs e)
{
// _selectCounter is the number of selection changed events that have fired.
// If you are really paranoid, you will want to make sure that if
// _selectCounter reaches MAX_INT, that you reset it to zero.
int mySelectCount = ++_selectCounter;
// start the timer and wait for it to finish
await Task.Delay(SELECT_TIMER_LENGTH);
// If equal (mySelectCount == _selectCounter),
// this means that NO select change events have fired
// during the delay call above. We only do the
// programmatic selection when this is the case.
// Feel free to adjust SELECT_TIMER_LENGTH to suit your needs.
if (mySelectCount == _selectCounter)
{
this.TextBox2.Select(this.TextBox1.SelectionStart, this.TextBox1.SelectionLength);
}
}

How to disable CheckedListBox but enable scrolling winforms

I know that the question might seems to easy, but I can't find solution. I have CheckdListBox in my form. I have list of checkboxes inside. If I do that:clbxMyControl.Enabled = false; then I can't scroll to see all items. How to allow scrolling on disabled CheckedListBox?
Thanks!
Instead of disabling the control you should change it's SelectionMode like this:
checkedListBox1.SelectionMode = SelectionMode.None;
The user won't be able to select an item but will be allowed to scroll
You can prevent the user from checking items with the ItemCheck event:
bool listEnabled = true;
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e) {
if (!listEnabled) e.NewValue = e.CurrentValue;
}
But do make sure that it is obvious that checking no longer works:
private void setListEnabled(bool enabled) {
listEnabled = enabled;
if (listEnabled) checkedListBox1.BackColor = Color.FromKnownColor(KnownColor.Window);
else checkedListBox1.BackColor = Color.FromKnownColor(KnownColor.Control);
}
The ListBox has only one handle in the Windows API which is set to enabled or disabled. Therefore there is no native way to enable the scrolling but the items.
You might be able to build your own control that has the requested behavior but you will have to paint it your self from scratch which might be a lot of work or you find a third party control that supports the behavior.
Or you can not use the enabled property but change the background/font color to make it look disabled and set:
checkedListBox1.SelectionMode = SelectionMode.None;
Another alternative might be the DataGridView. It is much more powerful, I'm not sure if it disables the scrollbar, too but if it does you are able to make the cells readonly and color them.
You can use this code to make CheckedListBox scrollable.
clbxMyControl.SelectionMode = SelectionMode.None;
Because Enable=false; make control unscrollable because this make the container of checkbox disabled.

AutoCompleteComboBox Arrow Up/Arrow Down keys to scroll list

I created a simple AutoCompleteBox in my WPF app and it loads great with code intercepting the Populate event, but when the list pops up and I hit the arrow down key and get to the end of the list the vertical scroll bar doesn't scroll.
The values keep changing in the field like it is scrolling through them, but the scroll bar doesn't move.
If I use the mouse it scrolls fine.
I just need the arrow key to scroll it.
Any ideas/suggestions?
I am new to WPF and have searched forever for this fix.
Attach a SelectionChanged event and then, inside the handler:
private void AutoCompleteBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
AutoCompleteBox box = (AutoCompleteBox)sender;
ListBox innerListBox = (ListBox) box.Template.FindName("Selector", box);
innerListBox.ScrollIntoView(innerListBox.SelectedItem);
}
I see the same behavior. I found a post on codeplex talking about a different issue but at the bottom of the post they have a class AutoCompleteBoxEx that supports ScrollIntoView, so you can hook up the SelectionChanged even and this should get you the behavior you want. I have no idea why this is not baked in. I have had a chance to test out the posted code.
Update
Just pasted the code from the post into a class and used it in the XAML by changing AutoCompleteBox to AutoCompleteBoxEx and adding namespace for AutoCompleteBoxEx and it worked fine. You don't have to specify any event in the XAML, nor do you need to add any code to the code behind.

Redraw Toolstrip based on selections

I have been asked to write c# winforms app that will give users the ability to select options from a checkbox list and have it automatically redraw/repaint a toolstrip with the selected items.
I am new to winforms so I am not sure how to approach it. Should I be using the BackgroundWorker Process? Invalidate()?
Just alittle confused.
Any assistence of pointing in the right direction would be appreciated.
You probably don't want a BackgroundWorker as that's run on a non-UI thread and would cause problems when you try to modify the toolstrip (you can only work with the UI on the thread the UI was created on). Handle the CheckedChanged events on the checkboxes and then add or remove items from the toolstrip. The repainting should be automatic.
You need to keep tooltips for all options some where (if Tag property of checkboxes is free the put it there). Then when an option is selected or deselected, you need to update tooltips.
Let's suppose you are adding all the checkboxes in a IList. then things will work as follows:
private IList<CheckBox> options= new List<CheckBox>();
private void UpdateTTip()
{
toolTip1.RemoveAll();
foreach (CheckBox c in options)
{
if (c.Checked)
toolTip1.SetToolTip(c, c.Tag.ToString());
}
}
Now you need to call this on checkedchanged event of options check boxes:
private void chk_CheckedChanged(object sender, EventArgs e)
{
UpdateTTip();
}
A toolstrip contains controls by itself - it does not just "paint" buttons you can press. In order to have the toolstrip display different buttons depending on different conditions, you can:
Clear the toolstrip items and re-create the ones that are needed in the current context in code when items are checked in the list you mentioned
Add all the items and design time (with property Visible = false) and only set the necessary ones to Visible = true upon selection in your check listbox
No need to do any painting :-)

Categories