How to make ListView change ItemControl to VirtualizingStackPanel at runtime - wpf

OK, so, I have a ListView-derived control that changes Grouping and ItemsSource on the fly. When I group such that the scrollbars dissapear, and then change my ItemsSource to a different ICollectionView, my scrollbars do not return.
The basic problem is that ListView changes to a VirtualizedStackPanel when grouping is activated and does not change back when grouping is de-activated.
I don't mind that virtualization is disabled when grouping--this is not a problem. What I need is a way to make the ListView regenerate it's ItemPanel when I change the ItemsSource.

Could you add an event handler to the SourceUpdated event and then reset the ItemsPanelTemplate to a template defined in your Resources?
Something like:
public MyWindow()
{
InitializeComponent();
MyListView.SourceUpdated += new EventHandler<DataTransferEventArgs>( OnSourceUpdated );
}
void OnSourceUpdated( object sender, DataTransferEventArgs e )
{
MyListView.ItemsPanel = (ItemsPanelTemplate)Resources["MyItemsPanelTemplate"];
}

Related

WPF: how to auto scroll to my first ListViewItem when form load

So i am build simple Clipboard manager.
Every ListViewItem come from Clipboard.GetText and my application minimize to Tray and when double click on its Icon the application jump and i want to focus become on the first ListViewItem in order to be able to navigate with Up & Down arrows.
This is my ListView:
ListView myListView;
Model List:
public ObservableCollection<string> Clipboards
Window Loaded event:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (viewModel.Clipboards.Count != 0)
myListView.ScrollIntoView(myListView.Items[0]);
myListView.Focus();
}
So currently when i open int the first time the application, the Focus is not on any ListViewItem and in the next time the focus is on the last selected/click ListViewItem and not on the first one.
It looks like you have your ViewModel in the code behind. This is not good MVVM standard.
Maybe this could be helpful
How can I set the focus to a ListBox properly on load if it uses databinding?
From what I see you are not focusing any ListViewItem but the ListView itself. I think this is your mistake. To focus the item you have to get it's container. The objects in the ItemsSource are actually the data itself and no the UIElement to render. To draw this data or add it to the visual tree for rendering, the ItemsControl will generate a container for the data e.g. a ListViewItem. Only the UIElement can receive focus, that's why the UIElement exposes the Focus() method. You have to use the ItemContainerGenarator to retrieve this container for your data:
ListView myListView;
(myListView.ItemsPanel as VirtualizingPanel)?.BringIndexIntoViewPublic(0);
myListView.Dispatcher.Invoke(new Action(
() =>
{
ListBoxItem dataContainer = myListView.ItemContainerGenerator.ContainerFromIndex(0) as ListBoxItem;
dataContainer?.Focus();
}), DispatcherPriority.ContextIdle);
This example will move the focus to the first element in the ListView.

How to attached events on DataGridColumnHeader in Custom DataGrid in code behind?

I attached event "DataGridColumnHeader_MouseRightButtonDown" on Loaded event to open context menu to display column names. It's working fine.
private void dtaGrd_Loaded(object sender, RoutedEventArgs e)
{
columnHeaders = TreeHelper.GetVisualChildCollection<DataGridColumnHeader>(this);
foreach (DataGridColumnHeader columnHeader in columnHeaders)
{
columnHeader.MouseRightButtonDown += DataGridColumnHeader_MouseRightButtonDown;
}
}
But when I uncheck column then Visibility of "Name" column to be Collapsed. Again, I checked that column to set Visibility to "Visible" then "DataGridColumnHeader_MouseRightButtonDown" event is not working.
Is my Implementation is wrong or DataGridColumnHeader will create new instance when visibility is change?
Yes, I found that when visilbity of DataGridColumnHeader is changed then it's Loaded event is also fires. So, We have to bind even handlers on loaded event of DataGridColumnHeader.
On which event of datagrid I come to know that DataGridColumnHeader is Loaded ?
Or Where I have to attach DataGridColumnHeader Loaded Event ?
First, you should be known that each time you change visibility of any control, its 'Loaded' event executes.
Here in your case, you're providing handlers to the column headers of the datagrid by finding from visual collection of the DataGrid.
So, once you flip Visibility of the column from Visible to Collapsed it'll unload column from datagrid and again, when you make it Visible it loads with default style provided to the header.
And here comes the main problem that handlers you binded to the column Header will not be found as column got Reset
My opinion is to give try to Custom Commands or you've to manage attaching/detaching handlers while fliping visibility.
Thanks :)

Set VisualState of combobox when a item is selected in Xaml (Silverlight)

When my combobox expands and I select an item, I want the combobox to change visual state(it is highlighted). This will signify something is selected. I tried various VisualStates but none of them would trigger in this scenario. How can I achieve this? Thanks.
The standard ComboBox simply doesn't have states to distinguish between having something selected and having nothing selected.
There are a number of ways to go about solving the underlying problem, and it depends mostly on the answer to the following question:
Do you really need to change the visual appearance of the ComboBox itself or does it suffice to style the selected item more prominently?
If it's the latter, you're best served with the rather easy way of using a custom control template for the ComboBoxItems.
If you really want to style the ComboBox itself that way, there are two options I can think of:
A) Add custom states to a ComboBox with a custom template.
Copy your ComboBox's control template and add another state group to the already present states. Both of this is typically done in Expression Blend.
After that you can update the new states in code with
VisualStateManager.GoToState(this, "Selected", true);
for example. You will have to set those states yourself when the first item is chosen. This could be done on the SelectionChanged event.
B) Derive from ComboBox
If you want to use the control in this way often, it might be worthwhile to derive from ComboBox to make your own custom control.
It would look somthing like this:
[TemplateVisualState(Name = "SelectedStates", GroupName = "Unselected")]
[TemplateVisualState(Name = "SelectedStates", GroupName = "Selected")]
// ... (more attributes copied from the ComboBox ones)
public class MyComboBox : ComboBox
{
public MyComboBox()
{
SelectionChanged += HandleSelectionChanged;
DefaultStyleKey = typeof(MyComboBox);
}
void HandleSelectionChanged(object sender, SelectionChangedEventArgs e)
{
VisualStateManager.GoToState(this, SelectedItem != null ? "Selected" : "Unselected", true);
}
}
And you would then need a default style based on the default ComboBox style (or whatever you usually use).
Note that I didn't test this in any way.

How to correctly resize scrollbar when underlying collection of a WPF ListView changes?

How to correctly resize scrollbar when underlying collection of a WPF ListView changes?
I have a WPF ListView bound to an observeable collection with several thousand items. When a large number of these are removed the view seems to only show the last item. When I move the position in the view with the thumbbar, the thumbbar resizes to reflect the new collection size. Is it possible to force the ListView and Scroll bar to synchronise when the collection changes?
I have found a work-around if anyone else has this problem.
The following code example shows the items source of the ListView bening changed on the first line. The following lines show the workaround which is just to scroll back to the first item.
this.ListViewResults.ItemsSource = this.itemsFiltered;
object firstItem = this.ListViewResults.Items.GetItemAt(0);
if(firstItem == null)
{
return;
}
this.ListViewResults.ScrollIntoView(firstItem);
Strange behaviour!!
I would try setting the binding context (Context) of the ListView to null, and then the same list again in order to refresh the bindings.
I have a different workaround which requires subclassing ListView. It's a bit more work but the result is better than just scrolling to the first item. But you'll need to adapt the ListView template such that the ScrollViewer in the template has a name (here PART_ScrollViewer) or you use another way to get the ScrollViewer object.
public class BetterListView : ListView
{
ScrollViewer sv;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
//Get the scrollviewer in the template (I adapted the ListView template such that the ScrollViewer has a name property)
sv = (this.Template.FindName("PART_ScrollViewer", this)) as ScrollViewer;
}
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
//Prevent the bug where the ListView doesn't scroll correctly when a lot of items are removed
if (sv != null && e.Action == NotifyCollectionChangedAction.Remove)
{
sv.InvalidateScrollInfo();
}
}
}

WPF DataGrid how to get when ItemsSource updates

Which event fires when DataGrid's source is updating? I've tried DataContextChanged and SourceUpdated but it never worked out.
Actually I need a simple thing. I want, if there is a new row comes, scroll the GridView's scrollbar down to the bottom to see what it was.
I had the same problem and I manage it this way
DataGrid myGrid = new DataGrid();
CollectionView myCollectionView = (CollectionView)CollectionViewSource.GetDefaultView(myGrid.Items);
((INotifyCollectionChanged)myCollectionView).CollectionChanged += new NotifyCollectionChangedEventHandler(DataGrid_CollectionChanged);
You then need to implement the logic in the event handler DataGrid_CollectionChanged.
Set NotifyOnTargetUpdated = true for the ItemsSource binding and handle TargetUpdated event. If you've multiple bindings, then look for DataTransferEventArgs Property to find out if the target is ItemsSource or not.
If you are trying to have the grid refresh when something is added to the database itself, that's not going to happen. I'm more familiar with WinForms than WPF but I'm assuming there is no magical way to keep a grid in sync with the database without writing some background process that continuously checks for database changes.
If you are updating the actual data source of the grid (ex. Collection) then that will update the grid.
For my part i've used SelectionChange notification which raise each event Del/Add/Edit/Select
It's work very well
private void dataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Console.WriteLine("hi");
}

Resources