I cannot get this ItemsControl to virtualize properly. Debugging shows that the collection is initialized quickly, but all the items are being added to the control instead of a subset (I simply put a TracePoint in the TextBox_Initializeevent in theUserControl` that makes up the item).
Note: I have looked at other, similar questions, but have been unable to solve this issue with those answers.
The ViewModel:
public class ImportInformationViewModel : CommandViewModel
{
public ImportInformationViewModel()
{
this.PropertyChanged += ImportInformationViewModel_PropertyChanged;
}
private ObservableCollection<SingleTransactionViewModel> mTransactions;
public ReadOnlyObservableCollection<SingleTransactionViewModel> Transactions
{
get
{
if (mTransactions == null)
mTransactions = new ObservableCollection<SingleTransactionViewModel>();
var filtered = mTransactions.Where(trans => !trans.IgnoreTransaction)
.OrderBy(trans => trans.DateStamp)
.ThenBy(trans => trans.TransactionName)
.ThenBy(trans => trans.TransactionDetail);
return new ReadOnlyObservableCollection<SingleTransactionViewModel>(new ObservableCollection<SingleTransactionViewModel>(filtered));
}
}
private void ImportInformationViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "TransactionFileName")
{
if (File.Exists(mTransactionFileName))
{
mTransactions.Clear();
// Process File (Not Shown)
mTransactions.Add(new SingleTransactionViewModel()
{
DateStamp = date,
TransactionDetail = someText;
});
}
}
if (e.PropertyName != "Transactions")
NotifyPropertyChanged("Transactions");
}
}
SingleTransactionViewModel is just another class that implements INotifyPropertyChanged. Nothing special.
Here is the control containing the ItemsControl
<UserControl x:Class="ImportInformationView">
<UserControl.Resources>
<CollectionViewSource x:Key="TransactionsData" Source="{Binding Transactions}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="YearAndMonth" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<BooleanToVisibilityConverter x:Key="booleanToVisibility" />
</UserControl.Resources>
<ItemsControl ItemsSource="{Binding Source={StaticResource TransactionsData}}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
ScrollViewer.CanContentScroll="True"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
MinHeight="20">
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<GroupBox Padding="5" Margin="2,5">
<GroupBox.Header>
<Border Background="Black"
CornerRadius="4">
<TextBlock Text="{Binding Name}" />
</Border>
</GroupBox.Header>
<ItemsPresenter />
</GroupBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ItemsControl.GroupStyle>
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer CanContentScroll="True">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid IsItemsHost="True" Columns="2" Grid.IsSharedSizeScope="True" VirtualizingStackPanel.IsVirtualizing="True" />
<!--<VirtualizingStackPanel IsItemsHost="True" />-->
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<views:SingleTransactionView Margin="4,6" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
And I've boiled the SingleTransactionView down to something very simple:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="ExpanderColumn" />
<ColumnDefinition Width="*" SharedSizeGroup="TransactionNameColumn" />
<ColumnDefinition Width="Auto" SharedSizeGroup="DateColumn" />
<ColumnDefinition Width="Auto" SharedSizeGroup="OptionsColumn" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="1"
Grid.Row="0"
Initialized="TextBlock_Initialized"
Text="{Binding TransactionName}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="14"
FontWeight="Bold"
Margin="4"/>
</Grid>
This is probably due to your use of grouping. By default, enabling grouping effectively switches off virtualization. However, as of .NET 4.5, you should be able to regain this functionality via the VirtualizingPanel.IsVirtualizingWhenGrouping property.
Excerpt from this blog post on WPF enhancements in .NET 4.5:
In WPF 4.0, you lost virtualization when grouping is done on the collection you display. I repeat : Grouping = no virtualization in WPF 4.0. This is still the default behavior of WPF 4.5, but you can turn on the virtualization by using the IsVirtualizingWhenGrouping attached property of the VirtualizingPanel class. When this is done, you benefit of all the already described advantages of virtualization.
Here is how you can enable it via XAML:
<ListBox ItemsSource="{Binding Persons}"
ItemTemplate="{StaticResource PersonDataTemplate}"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.IsVirtualizingWhenGrouping="True">
<ListBox.GroupStyle>
<GroupStyle HeaderTemplate="{StaticResource GroupHeaderTemplate}" />
</ListBox.GroupStyle>
</ListBox>
Sounds like setting VirtualizingPanel.IsVirtualizingWhenGrouping="True" alongside your other virtualization-related properties should give you the behavior you want.+
Related
I have a ListView defined in a XAML interface which is bound to a collection.
The list view's DataTemplate features a WebView. I need this WebView to size to its contents, which means that after the ListView is bound, and elements are created, and those elements are bound, I need to execute a fragment of code on each WebView.
I've already taken care of getting the WebView to bind, and I have the code which will size a WebView. I simply need to know how to execute it; where to put it; how to get the WebViews and when to try to get them.
EDIT:
Here's my list view.
<ListView Opacity="{Binding IsRefreshing, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BooleanToGhost}}" Grid.Row="0" Name="listView" ItemsSource="{Binding Messages}" IsItemClickEnabled="False" SelectionMode="Single">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Rectangle Grid.ColumnSpan="2" Grid.RowSpan="2" Fill="White" RadiusX="12" RadiusY="12" />
<TextBlock Foreground="#FFAAAAAA" Grid.Row="0" Grid.Column="0" Margin="8" Text="You" HorizontalAlignment="Left" FontSize="11" Visibility="{Binding IsFromStaff, Mode=OneTime,Converter={StaticResource BooleanToInvisibility}}" />
<TextBlock Foreground="#FFAAAAAA" Grid.Row="0" Grid.Column="0" Margin="8" Text="Staff" HorizontalAlignment="Left" FontSize="11" Visibility="{Binding IsFromStaff, Mode=OneTime,Converter={StaticResource BooleanToVisibility}}" />
<TextBlock Foreground="#FFAAAAAA" Grid.Row="0" Grid.Column="1" Margin="8" Text="{Binding Timestamp, Mode=OneTime}" HorizontalAlignment="Right" FontSize="11" />
<WebView local:MyProperties.HtmlString="{Binding Body}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="8" ScrollViewer.VerticalScrollBarVisibility="Disabled" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="SelectionChanged">
<core:InvokeCommandAction Command="{Binding ShowCaseCommand, Mode=OneWay}" CommandParameter="{Binding ElementName=listView, Path=SelectedItem}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ListView>
The solution is something called "Behaviors".
We see in the code snippet that the DataTemplate contains a WebView, which is the item I want to affect. So I add a Behavior to the WebView, which is a class with code. The class looks like so:
public class WebViewSizeBehavior : DependencyObject, IBehavior
{
public DependencyObject AssociatedObject { get; private set; }
public void Attach(DependencyObject associatedObject)
{
var control = associatedObject as WebView;
if (control == null)
throw new ArgumentException(
"WebViewSizeBehavior can be attached only to WebView.");
AssociatedObject = associatedObject;
control.LoadCompleted += Control_LoadCompleted;
}
private void Control_LoadCompleted(object sender, Windows.UI.Xaml.Navigation.NavigationEventArgs e)
{
var control = (WebView) AssociatedObject;
var resizeTask = control.ResizeToContent();
}
public void Detach()
{
var control = (WebView) AssociatedObject;
control.LoadCompleted -= Control_LoadCompleted;
AssociatedObject = null;
}
}
My namespaces and XAML are rigged up such that this class is reachable only the local namespace. How this is done is beyond the scope of this answer. So, given that, I can amend the XAML like so:
<ListView.ItemTemplate>
<DataTemplate>
…
<WebView …>
<interactivity:Interaction.Behaviors>
<local:WebViewSizeBehavior />
</interactivity:Interaction.Behaviors>
</WebView>
</DataTemplate>
</ListView.ItemTemplate>
I've left out content which is beyond the scope of this thread. Information about fitting a WebView to its content, as well as namespaces and XML namespaces, is available elsewhere.
I have a TabControl bound to an ObservableCollection property of view models, TabViewModelsCollection.
When another property gets set from within the view models, DeviceState, I'd like to raise a property changed event and tell my TabControls ItemSource to refresh.
The problem is my ItemSource is an ObservableCollection of ViewModels and when I call RaisePropertyChanged("TabViewModelsCollection"); nothing gets updated.
Furthermore my tab views contain multiple user controls and bindings.
The scenario that is supposed to play out is: A device is located on the network, data is collected, and the tabs of device information should then be updated.
Currently my TabControl only updates when I select a different device then select the device whos information I want to see. Think left panel list of devices, right panel with tabs of device info.
Let me know what part of the code you guys might want to see, my codebase is quite large so it would be hard to post it.
Here is where the TabControl is defined in my view:
<!-- Devist List Controls -->
<Grid DockPanel.Dock="Left" Margin="5,5">
<local:DeviceListView DataContext="{Binding DeviceListViewModel}" Grid.Row="0"/>
</Grid>
<GroupBox Header="Device Information" DockPanel.Dock="Right" Margin="0,0,5,5">
<TabControl IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding DeviceListViewModel.SelectedDevice.TabViewModelsCollection}" SelectedItem="{Binding DeviceListViewModel.SelectedDevice.SelectedTabItemVm}" >
<TabControl.Resources>
<DataTemplate DataType="{x:Type vms:HomeViewModel}">
<local:HomeTab/>
</DataTemplate>
<DataTemplate DataType="{x:Type vms:ConfigurationViewModel}">
<Grid>
<local:ConfigurationFileView Visibility="{Binding Configuration, TargetNullValue=Collapsed, FallbackValue=Visible}"/>
<local:ErrorTab Visibility="{Binding Path= Configuration, TargetNullValue=Visible, FallbackValue=Hidden}"/>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type vms:ExpansionModulesViewModelFactory}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="35"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<DockPanel >
<local:ExpansionModulesList Title="Discovered/Enumerated"
DataContext="{Binding DiscoveredModules}"
/>
<GridSplitter Width="5"/>
<local:ExpansionModulesList Title="User Action Required"
DataContext="{Binding FaultyModules}"
/>
</DockPanel>
</StackPanel>
<DockPanel Grid.Row="1">
<StackPanel Orientation="Horizontal" FlowDirection="RightToLeft" Margin="5" IsEnabled="{Binding IsCommandEnabled}">
<Button Content="Cancel" HorizontalAlignment="Right"
Command="{Binding CancelExpansionCommand }"
ToolTip="Revert all local modifications by refreshing data from the controller." />
<Separator Width="10"/>
<Button Content="Apply" HorizontalAlignment="Center"
Command="{Binding ApplyExpansionCommand }"
ToolTip="Apply all changes to the controller." />
<Separator/>
</StackPanel>
</DockPanel>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type vms:LogViewModel}">
<local:LogView />
</DataTemplate>
<DataTemplate DataType="{x:Type vms:SignalStrengthViewModel}">
<local:SignalStrengthView />
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="IsEnabled" Value="{Binding IsEnabled}" />
<Setter Property="Header" Value="{Binding Name}" />
</Style>
</TabControl.ItemContainerStyle>
EDIT: I want to be able to call raised property changed on this and have it refresh all of my tab views..
<TabControl IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding DeviceListViewModel.SelectedDevice.TabViewModelsCollection}" SelectedItem="{Binding DeviceListViewModel.SelectedDevice.SelectedTabItemVm}" >
RaisePropertyChanged("TabViewModelsCollection");
Hi please try the next solution:
VM code redaction
private State _deviceState;
private ObservableCollection<object> _tabViewModelsCollection;
public State DeviceState
{
get { return _deviceState; }
set
{
_deviceState = value;
RaisePropertyChanged("DeviceState");
UpdateTabViewModelsCollection();
}
}
public ObservableCollection<object> TabViewModelsCollection
{
get
{
return _tabViewModelsCollection ??
(_tabViewModelsCollection = new ObservableCollection<object>(GetDeviceData()));
}
}
private void UpdateTabViewModelsCollection()
{
_tabViewModelsCollection = null;
RaisePropertyChanged("TabViewModelsCollection");
}
private List<object> GetDeviceData()
{
//implement here the data collection process
throw new NotImplementedException();
}
Xaml redaction (define the UpdateSourceTrigger)
ItemsSource="{Binding DeviceListViewModel.SelectedDevice.TabViewModelsCollection, UpdateSourceTrigger=PropertyChanged}"
Let me know if it was helpful.
Regards.
I'm having issues with scrolling through ListViews in my Windows Phone 8.1 App. Short lists scroll just fine, scrolling smoothly however as soon Virtualization kicks in the entire ListView "wobbles" to the left slightly, but noticeable enough to be annoying.
I've tried remove all the transitions to no effect as well as having items load incrementally to no success. Setting the item panel to a StackPanel (removing virtualization) fixes the issue but is not preferable.
My listviews are binding to a property in the DefaultViewModel that comes with the Basic Page Template.
What am I doing wrong and what are causing my ListViews to exhibit this behavior?
XAML:
<ListView x:Name="searchResultsList" IsItemClickEnabled="True" ItemClick="ListView_ItemClick" ItemsSource="{Binding searchResults}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="0,0,0,20" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Width="80" Height="80">
<Image Source="{Binding Image}" />
</Border>
<StackPanel Grid.Column="2">
<TextBlock Text="{Binding PodcastTitle}" TextWrapping="WrapWholeWords" FontSize="{StaticResource TextStyleExtraLargeFontSize}" />
<TextBlock Text="{Binding LastUpdated, Converter={StaticResource dateConverter}}" Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}" />
<TextBlock Text="{Binding PodcastArtist}" TextWrapping="WrapWholeWords" Style="{ThemeResource ListViewItemContentTextBlockStyle}" />
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
So this seems to be an OS issue, as evidenced in this thread on the MS forums: http://social.msdn.microsoft.com/Forums/en-US/9a363d33-5760-4d38-9c81-84259c4edcbe/listview-jiggles-horizontally-when-large-item-about-to-scroll-in-or-out-in-windows-phone-81-preview?forum=WindowsPhonePreviewSDK&prof=required.
The issue does indeed lie in virtualization, with items that have no fixed width. Using star as the width or making the horizontal alignment stretch won't work so the only solution that takes account orientation and resolution was to bind the width to the ListView's container's ActualWidth property:
<Grid x:name="contentRoot" Margin="19,9.5,19,0">
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Width={Binding ActualWidth, ElementName=contentRoot} />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
The first element in the listview is not displayed because the ActiualWidth of the Grid is 0 in the first second, when loaded the page. This is the solution, that's working for me:
<Grid x:Name="contentRoot" Margin="20">
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<Grid MinWidth="{Binding ActualWidth, ElementName=contentRoot}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
This is a really annoying bug. I can't understand why this is not fixed since years now.
Imho stretching items in a vertical scrolling listview is a very basic feature and should work 100%.
A possible workaround is also this snipped, which should also be aware of size changes :
public class StrechItemsListView : ListView
{
public StrechItemsListView()
{
SizeChanged += StrechItemsListView_SizeChanged;
}
private void StrechItemsListView_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (ItemsPanelRoot != null)
{
ItemsPanelRoot.Width = e.NewSize.Width;
}
}
}
Changing the xaml only to a custom listview type is less work and cleaner then edit every datatemplate etc. Just my 2 cents.
My Practice seems to work. At least in WP8.1.
Just set the ItemsPanelTemplate in the <ListView></ListView> Block explicitly, but not use
Style="{StaticResource ListViewStyle1}" or something else.
Sample Code:
<ListView ItemsSource="{Binding RadioList}" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollMode="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollMode="Auto">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="10,3,10,0">
<TextBlock Text="{Binding RadioName}" FontSize="15" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Vertical" Width="{Binding PhoneWidth}"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
The HorizontalContentAlignment and Width of VirtualizingStackPanel settings are used to center the content in the ListView .You can move these settings freely.I don't know why, but it does work.
This was fixed on Windows 10 for Windows 8.1 apps
I'm new to XAML. I searched about ItemsControl and I found a tutorial, which is easy to understand, but the problem is that it is not work in WinRT.
Tutorial: https://rachel53461.wordpress.com/2011/09/17/wpf-itemscontrol-example/
I tried to use TargetType in Style tag, however, in runtime I got an exception.
<ItemsControl ItemsSource="{Binding MyCollection}">
<!-- ItemsPanelTemplate -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- ItemContainerStyle -->
<ItemsControl.ItemContainerStyle>
<Style TargetType="TextBox">
<Setter Property="Grid.Column"
Value="{Binding xIndex}" />
<Setter Property="Grid.Row"
Value="{Binding yIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>
<!-- ItemTemplate -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Background="{Binding color}" Text="{Binding xIndex,Mode=OneWay}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Your problem is here:
<Style TargetType="TextBox">
this is what is should be:
<Style TargetType="ContentPresenter">
The ItemContainer for an ItemsControl is a ContentPresenter (unless a specific item is added to the ItemsControl).
So your view hierarchy looks something like this (assuming you didn't change the ItemsPanel to something other than a StackPanel):
<StackPanel>
<ContentPresenter>
<TextBox/>
</ContentPresenter>
</StackPanel>
Edit:
As Scott pointed out in the comments, this solution doesn't actually work for WinRT. I did something similar and you can probably modify it to do the appropriate binding:
public class CustomItemsControl : ItemsControl
{
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
FrameworkElement source = element as FrameworkElement;
if (source != null)
{
source.SetBinding(Canvas.LeftProperty, new Binding { Path = new PropertyPath("X"), Mode = BindingMode.TwoWay });
source.SetBinding(Canvas.TopProperty, new Binding { Path = new PropertyPath("Y"), Mode = BindingMode.TwoWay });
}
}
}
This binds the Canvas.LeftProperty to the X property on each item in the collection and similarly the Canvas.TopProperty and the Y property.
I have a list box that displays the results of a TFS Query. I want to change the style of the ListBoxItem in the code behind to have the columns that are included in the query results.
The style for the ListBoxItem is defined in my Windows.Resoruces Section. I have tried this:
public T GetQueryResultsElement<T>(string name) where T : DependencyObject
{
ListBoxItem myListBoxItem =
(ListBoxItem)(lstQueryResults.ItemContainerGenerator.ContainerFromIndex(0));
// Getting the ContentPresenter of myListBoxItem
ContentPresenter myContentPresenter =
myListBoxItem.Template.LoadContent().FindVisualChild<ContentPresenter>();
// Finding textBlock from the DataTemplate that is set on that ContentPresenter
DataTemplate myDataTemplate = myContentPresenter.ContentTemplate; <------+
T myControl = (T)myDataTemplate.FindName(name, myContentPresenter); |
|
return (T)myControl; |
} |
|
ContentTemplate is null ----------------------------------------------+
But the ContentTemplate is null. I got that code from here, then modified it with the LoadContent call (the orginal code gave null for the ContentPresenter).
Anyway. If you know a way to change an existing style in the code behind I would love to see it.
Specifics if you want them:
I am going for WrapPanel in my ListBoxItem Style. This is what I want to add the extra TextBlock items to.
Here is part of my style:
<!--Checkbox ListBox-->
<Style x:Key="CheckBoxListStyle" TargetType="ListBox">
<Style.Resources>
<Style x:Key="ListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Tag" Value="{Binding Id}"/>
<Setter Property="Background">
<Setter.Value>
<Binding Path="Type" Converter="{StaticResource WorkItemTypeToColorConverter}" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border BorderThickness="1" BorderBrush="#D4D4FF">
<Grid Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type WrapPanel}}, Path=ActualWidth}" ScrollViewer.CanContentScroll="True" Margin="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<Grid.Background>
<Binding Path="Type" Converter="{StaticResource WorkItemTypeToColorConverter}" />
</Grid.Background>
<CheckBox VerticalAlignment="Center" Grid.Column="0" IsChecked="{Binding IsSelected,
RelativeSource={RelativeSource TemplatedParent},
Mode=TwoWay}" Name="chkIsSelected" />
<WrapPanel Grid.Column="1" Margin="5,0,5,0" Name="QueryColumns">
<TextBlock VerticalAlignment="Center" Text="{Binding Id}" Name="txtID" />
<TextBlock VerticalAlignment="Center" Margin="5,0,5,0" Text="{Binding Title}" Name="txtTitle" />
</WrapPanel>
You're going against the grain here, trying to manipulate visual elements directly in code-behind. There's a much simple solution involving data binding.
I'll provide the general solution because I don't know the specifics of your solution.
Once you get your query results, create an enumeration that returns a column name, and a field value for each iteration.
Example:
class NameValuePair
{
public string Name { get; set; }
public object Value { get; set; }
}
public IEnumerable<IEnumerable<NameValuePair>> EnumerateResultSet(DataTable resultSet)
{
foreach (DataRow row in resultSet.Rows)
yield return EnumerateColumns(resultSet, row);
}
public IEnumerable<NameValuePair> EnumerateColumns(DataTable resultSet, DataRow row)
{
foreach (DataColumn column in resultSet.Columns)
yield return new NameValuePair
{ Name = column.ColumnName, Value = row[column] };
}
And in your code-behind, once you get your DataTable result set, do this:
myResultsList.ItemsSource = EnumerateResultSet(myDataTable);
The XAML might look like this:
<Window.Resources>
<DataTemplate x:Key="ColumnTemplate">
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="2" Padding="2">
<WrapPanel>
<TextBlock Text="{Binding Name}" Margin="0,0,5,0"/>
<TextBlock Text="{Binding Value}" Margin="0,0,10,0"/>
</WrapPanel>
</Border>
</DataTemplate>
<DataTemplate x:Key="RowTemplate">
<Grid>
<ItemsControl
ItemsSource="{Binding}"
ItemTemplate="{StaticResource ColumnTemplate}"
Margin="0,5,0,5"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox Name="myResultsList" ItemTemplate="{StaticResource RowTemplate}"/>
</Grid>
Sample output: