Forcing a ItemsControl panel to repaint - wpf

Alright, I have a ListBox that holds container controls. Each container control contains 1-X widgets and these widgets are laid out in a completely custom WrapPanel and it's working great. The size of the widgets is determined/updated from 2 factors:
The size of the parent listbox (updated if changed)
A view scale SliderBar (25%, 50%, 100%) - like a zoom button
If the listbox size is changed, the custom panel repaints and sizes of widgets are adjusted accordingly in my code. Working perfect.
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="5" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox Name="lstGroups" Grid.Column="0" ItemsSource="{Binding Path=TopLevelGroups}" BorderThickness="0" ScrollViewer.VerticalScrollBarVisibility="Hidden" SizeChanged="ListBox_SizeChanged" VerticalAlignment="Stretch"
SelectionMode="Extended"
Style="{ StaticResource ListBoxWithAutoScroll_Horizontal }"
>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
<StackPanel Grid.Row="2" HorizontalAlignment="Left" VerticalAlignment="Center" Orientation="Horizontal">
<Label Margin="5">View Scale:</Label>
<Slider Name="sldView" Minimum="25" Maximum="100" IsSnapToTickEnabled="True" TickPlacement="BottomRight" Ticks="25,50,100" Width="125" ValueChanged="Slider_ValueChanged"/>
</StackPanel>
<StackPanel Grid.Row="2" Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Right" DockPanel.Dock="Right">
<Button Command="{Binding Path=StopCommand}" Content="Stop" Height="30" Width="60" Margin="1" IsEnabled="{Binding Path=CanStop}" x:Name="btnStop" />
<Button Command="{Binding Path=RunCommand}" Content="Run" Height="30" Width="60" IsEnabled="{Binding Path=CanRun}" x:Name="btnRun" HorizontalAlignment="Right" Margin="1"/>
</StackPanel>
--Widget--
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0">
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="Edit" Command="{Binding Path=EditCommand}" IsEnabled="{Binding IsMonitorStopped}"/>
<MenuItem Header="Remove" Command="{Binding Path=RemoveCommand}" IsEnabled="{Binding IsMonitorStopped}"/>
</ContextMenu>
</StackPanel.ContextMenu>
<Image Source="" Margin="2" />
<Label Content="{Binding Path=Name}" Margin="5,0,0,0" VerticalAlignment="Center"/>
</StackPanel>
<ItemsControl Grid.Row="1" ItemsSource="{Binding Path=Entities}" Name="icEntities" >
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type vm:AppleViewModel}">
<ct:AppleTile Height="{Binding Path=AppleHeight}" Width="{Binding Path=AppleWidth}" Grid.ColumnSpan="2" Grid.RowSpan="2"/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:OrangeViewModel}">
<ct:OrangeTile Height="{Binding Path=OrangeHeight}" Width="{Binding Path=OrangeWidth}" Grid.ColumnSpan="2"/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:GrapeViewModel}">
<ct:GrapeTile Height="{Binding Path=GrapeHeight}" Width="{Binding Path=GrapeWidth}" />
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<!-- Custom Wrap panel -->
<co:MonitorPanel x:Name="mpMain"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
If the user moves the Slider bar for zoom in/zoom out - I need to be able to force the custom panels to re-render with the newly calculated widget sizes.
private void ListBox_SizeChanged(object sender, SizeChangedEventArgs e)
{
//Works perfect
this.ResetViewScale(e.NewSize);
}
...here comes trouble:
private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
...code here
this.ResetViewScale(new Size(this.myListBox.ActualWidth, this.myListBox.ActualHeight));
//How can I repaint my list/panel?
}
private void ResetViewScale(Size e)
{
Size newSize = new Size(e.Height / 2, e.Height / 2);
switch (App.ApplicationViewScale)
{
case ViewScale.Full:
Size full = new Size(newSize.Height, newSize.Height);
App.TileSize = new Size(full.Height, full.Width);
break;
case ViewScale.Half:
Size half = new Size(newSize.Height / 2, newSize.Height / 2);
App.TileSize = new Size(half.Height, half.Width);
break;
case ViewScale.Quarter:
Size quarter = new Size(newSize.Height / 4, newSize.Height / 4);
App.TileSize = new Size(quarter.Height, quarter.Width);
break;
default:
Size defaultToFull = new Size(newSize.Height, newSize.Height);
App.TileSize = new Size(defaultToFull.Height, defaultToFull.Width);
break;
}
}
I tried using all the Invalidate methods but nothing seems to be working. Sorta stuck...

Related

Listbox items not wrapping, tried almost everything. it might be due to ItemTemplate

**Tried adding wrap panel as parent, as child, as item template parent, but didn't work
**
<UserControl.Resources>
<DataTemplate x:Key="CredentialTemplate" DataType="{x:Type local:Credentials}">
<WrapPanel Width="800" Orientation="Horizontal">
<Grid Background="Red" Width="160" Height="160">
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding FileName}" FontWeight="Bold" Margin="0,0,10,0"/>
<Button Grid.Row="1" Grid.Column="0" x:Name="btnCopyUsername" Tag="{Binding Button}" Content="Copy Username" Click="OnCopyUsernameButtonClicked" Margin="10"/>
<Button Grid.Row="1" Grid.Column="1" x:Name="btnCopyPassword" Tag="{Binding Button}" Content="Copy Password" Click="OnCopyPasswordButtonClicked" Margin="10"/>
<Button Grid.Row="2" Grid.Column="0" x:Name="btnEdit" Tag="{Binding Button}" Content="Edit" Click="OnEditButtonClicked" Margin="10"/>
<Button Grid.Row="2" Grid.Column="1" x:Name="btnCopyBoth" Tag="{Binding Button}" Content="Copy Both" Click="OnCopyBothButtonClicked" Margin="10"/>
</Grid>
</WrapPanel>
</DataTemplate>
</UserControl.Resources>
<WrapPanel Width="900" Background="Yellow">
<ListBox ItemsSource="{Binding Credentials}" ItemTemplate="{StaticResource CredentialTemplate}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Background="Green" Margin="10"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</WrapPanel>
</UserControl>
It might be the grid control that not letting wrap, normally without item template it should work
Screenshot
I explored this issue with a simplified version of your markup.
I don't have a credentials class, but what's in the grids doesn't matter.
My markup has less wrappanels:
<Grid>
<Grid.Resources>
<DataTemplate x:Key="CredentialTemplate">
<Grid Background="Red" Width="40" Height="40">
</Grid>
</DataTemplate>
</Grid.Resources>
<ListBox ItemsSource="{Binding Points}"
ItemTemplate="{StaticResource CredentialTemplate}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"
Background="Green" Margin="10"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
I've got 30 Points in my viewmodel.
My grid is smaller
Wraps ok:

Nested ItemsControl Orientation

I have a nested ItemsControl. My data structure is an ObservableCollection of Campaigns which consist of a Campaign class and an observableCollection of Data counts (total, assigned, unassigned, closed). What I need is the following:
CAMPAIGN.NAME
TOTAL UNASSIGNED ASSIGNED CLOSED
CAMPAIGN.NAME
TOTAL UNASSIGNED ASSIGNED CLOSED
I am able to get the first part of this, but for some reason it will not honor the orientation for the second ItemsControl. What am I missing? My XAML is:
<ItemsControl x:Name="icCampaignChicklets" ItemsSource="{Binding CampChicks}" Grid.Row="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="gridContent">
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock x:Name="CampaignHeader" Height="20" Text="{Binding Path=Campaign.Name}" Grid.Row="1" VerticalAlignment="Top" TextWrapping="Wrap" HorizontalAlignment="Left" />
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="10">
<ItemsControl x:Name="icChicklets" ItemsSource="{Binding Path=Data}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Width="150" Height="140" Background="{Binding Background}" Cursor="Hand"
MouseLeftButtonDown="Chicklet_MouseLeftButtonDown"
>
<Grid x:Name="gridContent" Margin="8,4">
<TextBlock Text="{Binding Caption}" Foreground="White" FontSize="17" />
<TextBlock Text="{Binding CountCaption, Mode=OneWay}" Foreground="White" FontSize="45" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="7" />
<TextBlock Text="Ú" Foreground="#99ffffff" FontSize="30" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="3,5" FontFamily="Wingdings 3" />
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you want to change the orientation of the contents of an ItemsControl, set its ItemsPanel property like so:
<ItemsControl
...attributes...
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Wrapping it in a horizontally-oriented parent StackPanel will merely arrange it and its siblings horizontally, but in this particular case it has no siblings.

why aren't the buttons aligning horizontally in the stackpanel?

I have a list of buttons being generated from data, so there are a variable number of buttons. In an old version of the software I am overhauling, they used random custom controls but the result was that there was an infinitely growing horizontal scroller.
It appears I have the exact same XAML but it's not aligning each item horizontally, only vertically
<ScrollViewer Background="#33FFFFFF" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled" >
<StackPanel Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding Events}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Margin="10" HorizontalAlignment="Stretch" Background="#FF1B1B1B" BorderThickness="5" BorderBrush="White" Command="{Binding Source={StaticResource Locator}, Path=EventSelector.ViewEventCommand}" CommandParameter="{Binding }">
<Grid Name="tileGridButton" Height="600" Width="400" Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="5*"></RowDefinition>
<RowDefinition Height="5*"></RowDefinition>
</Grid.RowDefinitions>
<Border Grid.Row="0" Name="tileImageBorder" Margin="5" BorderThickness="1" VerticalAlignment="Stretch" HorizontalAlignment="Center">
<Image Name="tileImage" Margin="0" Source="{Binding ImageURL}"/>
</Border>
<Grid Grid.Row="1" VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="6*"></RowDefinition>
<RowDefinition Height="4*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Name="tileText" Margin="5" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" FontSize="30" FontWeight="Bold" Text="{Binding Title}" />
<TextBlock Grid.Row="1" Name="tileDescription" Margin="5" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" FontSize="25" FontWeight="Bold" Text="{Binding EventTimeBegin}" />
</Grid>
</Grid>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
First of all your StackPanel is pretty much useless because it only contains a single item, the ItemsControl.
Instead you need to change your ItemsControl so that it uses a StackPanel as its method of laying out items inside the ItemsControl...
<ItemsControl ...>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
<ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
.....

WPF Access grid items onClick

I'm new to wpf and have what I hope is a simple question.
I have created a simple template to display some grouped items
Currently when I click on one of the items ItemsClick is called (as expected)
private void itemGridView_ItemClick(object sender, ItemClickEventArgs e)
{
MyCustomClass selectedItem = (MyCustomClass)e.ClickedItem;
GridView g = (GridView)sender;
I can get a copy of the class object by casting e.ClickedItem and the grid from sender. But what I don't see is how to I get a reference to the custom items I added in the template for example if I wanted to change the text in the testName TextBlock ?
Template:
<DataTemplate x:Key="MyTestTemplate">
<Grid HorizontalAlignment="Left" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Grid.RowSpan="3">
<Image Source="{Binding ThumbnailUrl}" Stretch="Fill" Width="175" Height="175" />
</Border>
<Image Source="{Binding MyCustomImage}" Height="30" Width="30" Grid.RowSpan="3" Margin="0,0,0,30"/>
<Grid x:Name="ItemDetails" VerticalAlignment="Bottom" Height="75" Margin="0,0,0,2" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock x:Name="testName" Text="{Binding Name}" Grid.Row="0" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" MaxWidth="100" MaxHeight="80" TextWrapping="Wrap" HorizontalAlignment="Left" Style="{StaticResource TitleTextStyle}" Margin="5,0,5,0"/>
<TextBlock Text="{Binding Address}" Grid.Row="0" HorizontalAlignment="Right" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource
CaptionTextStyle}" TextWrapping="NoWrap" Margin="5,0,5,0" />
</Grid>
</Grid>
</DataTemplate>
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
Grid.RowSpan="2"
Padding="116,137,40,46"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
ItemTemplate="{StaticResource MyTestTemplate}"
SelectionMode="None"
IsSwipeEnabled="True"
IsItemClickEnabled="True"
RightTapped="itemGridView_RightTapped"
ItemClick="itemGridView_ItemClick" SelectionChanged="itemGridView_SelectionChanged">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Margin="1,0,0,6">
<Button
AutomationProperties.Name="Group Title"
>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding state}"/>
</StackPanel>
</Button>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid Orientation="Vertical" Margin="0,0,80,0"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</GridView.GroupStyle>
</GridView>
Ok I found the answer on msdn.
The link here http://msdn.microsoft.com/en-us/library/bb613579.aspx
The basic steps
get the selected item (I was responding to click so this info was easy), find the ContentPresenter within that Item, and then call FindName on the DataTemplate that is set on that ContentPresenter.

How can I create wpf Carousel withmy listview items?

I have horizontal wpf listview
<ListView Name="indexList" ItemsSource="{Binding}" HorizontalAlignment="Stretch" VerticalAlignment="Top" BorderBrush="Transparent" Background="CadetBlue" Width="450">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"></VirtualizingStackPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0" Margin="0,0,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Top">
<TextBlock>
<ContentControl Content="{Binding Field1}" FontSize="10" Foreground="GhostWhite" FontWeight="Bold" HorizontalAlignment="Stretch" VerticalAlignment="Top"></ContentControl>
</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Row="2" Grid.Column="0" Margin="0,0,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Bottom">
<TextBlock>
<ContentControl Content="{Binding Field2}" FontSize="9" Foreground="Bisque" HorizontalAlignment="Stretch" VerticalAlignment="Bottom"></ContentControl>
</TextBlock>
<TextBlock>
<ContentControl Content="{Binding Field3}" FontSize="9" Foreground="Coral" HorizontalAlignment="Stretch" VerticalAlignment="Bottom"></ContentControl>
</TextBlock>
</StackPanel>
<StackPanel Orientation="Vertical" Grid.Column="1" Grid.RowSpan="2" HorizontalAlignment="Stretch" VerticalAlignment="Center">
<Separator></Separator>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I want to scroll all items on left/right button click.
So that when i clicked on Prev/Left button listview items moves to left and on Right/Next button click listview items moves to right.
Like jQuery Carousel
When the Prev/Left button is clicked, call ScrollViewer.LineLeft().
When the Right/Next button is clicked, call ScrollViewer.LineRight().
The simplest way to access the ScrollViewer in a ListView is to do a top-down search of the visual tree, for example:
var scrollViewer = SearchVisualTree<ScrollViewer>(listView);
private T SearchVisualTree<T>(Visual vis) where T:Visual
{
if(vis==null) return null;
var range = Enumerable.Range(0, VisualTreeHelper.GetChildrenCount(vis));
return (
from i in range
let child = VisualTreeHelper.GetChild(vis, i) as T
where child!=null
select child
).Concat(
from j in range
let descendant = SearchVisualTree<T>(VisualTreeHelper.GetChild(vis, j) as Visual)
where descendant!=null
select descendant
).FirstOrDefault();
}

Resources