WPF Custom Control derived from ItemsControl fails to display bound data - wpf

I've created a custom control called MovableItemsControl, inheriting from ItemsControl, in order to override the GetContainerForItemOverride() method. My problem is that none of the objects in the bound collection are displaying. Currently, I'm binding to an OberservableCollection of strings, and I can see that they're in ItemsSource when I look through the debugger.
The custom control is shown below:
public class MovableItemsControl : ItemsControl
{
static MovableItemsControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MovableItemsControl), new FrameworkPropertyMetadata(typeof(MovableItemsControl)));
}
/// <summary>
/// Wraps each content object added to the ItemsControl in a NodeWrapper
/// </summary>
protected override DependencyObject GetContainerForItemOverride()
{
NodeWrapper nodeWrapper = new NodeWrapper();
return nodeWrapper;
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is NodeWrapper;
}
}
NodeWrapper is a UserControl consisting of a custom control derived from Thumb (MoveThumb) and a Label (the Label is just for testing).
<Style TargetType="{x:Type local:MovableItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MovableItemsControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Have you created the default Style for MoveableItemsControl with a ControlTemplate in the Generic.xaml file of the project containing the control? If not, there's nothing for the control to render when it loads.
UPDATE
The ControlTemplate for an ItemsControl needs to contain an ItemsPresenter as a placeholder for the items to be injected (similar to ContentPresenter for ContentControl). Your current template only has an empty Border.

I think you are missing inside your style ControlTemplate Border either:
a) An ItemPresenter (eg <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>) OR
b) A pannel with IsItemsHost set true (eg <StackPanel IsItemsHost="True"/>)

Related

How to get the ActualWidth and ActualHeight dynamically of a customcontrol?

I have a custom control, ccTextBlock placed inside a ScrollViewer. The customcontrol will be changing sizes (vertically) when different strings are sent to it through the binding. The custom control will remain on the display, but will change as text elsewhere on the screen is selected.
How can I obtain the actual width and height of the custom control only after and with each text string sent to it? (Using OnApplyTemplate() did not work as it seems to be called only once on the first construction of the custom control.)
Thanks for any replies.
<ScrollViewer VerticalScrollBarVisibility="Auto">
<Grid>
<wc:ccTextBlock Text="{Binding Text, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}" />
</Grid>
</ScrollViewer>
Update: Perhaps a better way to phrase this question would be "How to get the height of an element when it is inside a ScrollViewer". Here is the definition of ccTextBlock:
public class ccTextBlock : Control
{
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(ccTextBlock), new UIPropertyMetadata(null));
/// <summary>
/// Constructor
/// </summary>
static ccTextBlock()
{
// Initialize as lookless control
DefaultStyleKeyProperty.OverrideMetadata(typeof(ccTextBlock), new FrameworkPropertyMetadata(typeof(ccTextBlock)));
}
public override void OnApplyTemplate()
{
//Effectively apply the template
base.OnApplyTemplate();
Console.WriteLine(String.Format(" ActualHeight is {0}", this.ActualHeight.ToString()));
var x = this.FontSize;
}
}
Where Generic.xaml is:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCustomControlLibrary">
<Style TargetType="{x:Type local:ccTextBlock}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ccTextBlock}">
<!-- Control Layout -->
<TextBlock Text="{TemplateBinding Text}" TextWrapping="Wrap" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
ActualWidth and ActualHeight are the properties that contain the current width and height of the control.
If you are looking for an Event that notifies about changes, it would be the FrameworkElement.SizeChanged event. You could register for this event in the OnApplyTemplate implementation.

Expander.IsExpanded Binding breaking after first click

am probably doing something but can't figure out my problem. Any help would be really appreciated:
I am having a CustomControl called Section. A Section is collapsible and therefore its ControlTemplate contains an Expander. The Expanders IsExpanded-Property is bound to the Section's IsExpanded Property via TemplateBinding.
When setting IsExpanded on a Section the Expander collapses, but using the toggleButton within the Expander appears to break that binding. Probably by setting a local value to the Expander's IsExpanded-Property. Anyways, after changing the Expander state via Mouse the binding breaks and setting the Section's IsExpanded doesn't do anything.
This however does not happen when putting an Expander into a view and binding its IsExpanded-Property to some DP in the View.
Also notworthy: Snoop does not show any Bindings on the Expander's IsExpanded-Property. It only shows the Value-Source is ParentTemplate. As soon as I click the ToggleButton to Change IsExpanded the Value-Source changes to Local (possibly breaking the former Binding?)
Section.cs:
public class Section : Control
{
static Section()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Section), new FrameworkPropertyMetadata(typeof(Section)));
}
public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register(
"IsExpanded", typeof (bool), typeof (Section), new PropertyMetadata(default(bool)));
public bool IsExpanded
{
get { return (bool) GetValue(IsExpandedProperty); }
set { SetValue(IsExpandedProperty, value); }
}
}
Generic.xaml Style:
<Style TargetType="{x:Type local:Section}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Section}">
<Expander Header="Test" IsExpanded="{TemplateBinding IsExpanded}" >
<Rectangle Fill="Aqua" Height="200" Width="200" />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Any ideas?
So I found the answer which is pretty basic knowledge actually:
TemplateBindings are always ONEWAY no matter what the MetaData states...
Using:
IsExpanded="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsExpanded}"
fixed my problem...

QStackedLayout equivalent in WPF

I'm a quite experienced Qt programmer and I used QStackedLayout a lot to show different widgets in the main window. Can someone please point me to an equivalent construct in WPF: Is there such a thing like QStackedLayout? If not, how is this pattern used in WPF?
Basically I have a WPF Ribbon Application and if the Ribbon Group is switched the corresponding "widget" / XAML should be displayed in the remaining area ("content").
Thanks, dude.
There isn't a native panel or control that would do that, but you could leverage the TabControl to accomplish it. You'd need to use a custom Style, though like so:
<Style x:Key="NoTabsTabControlStyle" TargetType="{x:Type TabControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Border Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
KeyboardNavigation.TabNavigation="Local"
KeyboardNavigation.DirectionalNavigation="Contained">
<ContentPresenter x:Name="PART_SelectedContentHost"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Margin="{TemplateBinding Padding}"
ContentSource="SelectedContent"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled"
Value="false">
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then use it like:
<TabControl Style="{StaticResource NoTabsTabControlStyle}">
<TabItem Content="One" />
<TabItem Content="Two" />
</TabControl>
Then to display one set of content, you'd set SelectedIndex on the TabControl.
A bit late for topic starter but may be of some help to people who comes here searching for WPF version of QStackedLayout, like me.
I used the very simplified implementation of WPF layout example, throwing out virtually all things layout.
The component is based on StackLayout to allow for simple visual design, in design time it just behaves like normal stack panel.
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;
namespace org.tequilacat.stacklayout {
/// <summary>
/// QStackedLayout implementation for WPF
/// only one child is displayed extended to the panel size.
/// In design time it behaves like stack panel
/// </summary>
public class StackLayoutPanel : StackPanel {
private bool isDesignTime() {
return System.ComponentModel.DesignerProperties.GetIsInDesignMode(this);
}
private bool useBaseBehaviour() {
return isDesignTime();
}
// in runtime just return the given arg
protected override Size MeasureOverride(Size availableSize) {
if (useBaseBehaviour()) {
return base.MeasureOverride(availableSize);
}
return availableSize;
}
// in runtime arrange all children to the given arg
protected override Size ArrangeOverride(Size finalSize) {
if (useBaseBehaviour()) {
return base.ArrangeOverride(finalSize);
}
foreach (UIElement child in InternalChildren) {
child.Arrange(new Rect(finalSize));
}
return finalSize;
}
}
}
The XAML is
<Window ... xmlns:uilib="clr-namespace:org.tequilacat.stacklayout">
<uilib:StackLayoutPanel >
<StackPanel Name="projectPropertyPanel"> ... </StackPanel>
<StackPanel Name="configurationPanel"> ... </StackPanel>
<StackPanel Name="casePanel"> ... </StackPanel>
</uilib:StackLayoutPanel>
In Run time the visible component is chosen via Visibility property (here depends on my business logic, uiState can take 3 values activating one of panels). It's very basic, one can implement own CurrentPage property or so, I just kept it simple:
projectPropertyPanel.Visibility = (uiState == UiState.ProjectProperties) ?
Visibility.Visible : Visibility.Collapsed;
configurationPanel.Visibility = (uiState == UiState.ConfigurationSelected) ?
Visibility.Visible : Visibility.Collapsed;
casePanel.Visibility = (uiState == UiState.CaseSelected) ?
Visibility.Visible : Visibility.Collapsed;

CheckBox ListView SelectedValues DependencyProperty Binding

I am writing a custom control that is a ListView that has a CheckBox on each item in the ListView to indicate that item is Selected. I was able to do so with the following XAML.
<ListView x:Class="CheckedListViewSample.CheckBoxListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d">
<ListView.Style>
<Style TargetType="{x:Type ListView}">
<Setter Property="SelectionMode" Value="Multiple" />
<Style.Resources>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Border BorderThickness="{TemplateBinding Border.BorderThickness}"
Padding="{TemplateBinding Control.Padding}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}"
SnapsToDevicePixels="True">
<CheckBox IsChecked="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}}">
<CheckBox.Content>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</CheckBox.Content>
</CheckBox>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
</Style>
</ListView.Style>
</ListView>
I however am trying to attempt one more feature. The ListView has a SelectedItems DependencyProperty that returns a collection of the Items that are checked. However, I need to implement a SelectedValues DependencyProperty. I also am implementing a SelectedValuesPath DependencyProperty. By using the SelectedValuesPath, I indicate the path where the values are found for each selected item. So if my items have an ID property, I can specify using the SelectedValuesPath property "ID". The SelectedValues property would then return a collection of ID values. I have this working also using this code in the code-behind:
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
namespace CheckedListViewSample
{
/// <summary>
/// Interaction logic for CheckBoxListView.xaml
/// </summary>
public partial class CheckBoxListView : ListView
{
public static DependencyProperty SelectedValuesPathProperty =
DependencyProperty.Register("SelectedValuesPath",
typeof(string),
typeof(CheckBoxListView),
new PropertyMetadata(string.Empty, null));
public static DependencyProperty SelectedValuesProperty =
DependencyProperty.Register("SelectedValues",
typeof(IList),
typeof(CheckBoxListView),
new PropertyMetadata(new List<object>(), null));
[Category("Appearance")]
[Localizability(LocalizationCategory.NeverLocalize)]
[Bindable(true)]
public string SelectedValuesPath
{
get
{
return ((string)(base.GetValue(CheckBoxListView.SelectedValuesPathProperty)));
}
set
{
base.SetValue(CheckBoxListView.SelectedValuesPathProperty, value);
}
}
[Bindable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Category("Appearance")]
public IList SelectedValues
{
get
{
return ((IList)(base.GetValue(CheckBoxListView.SelectedValuesPathProperty)));
}
set
{
base.SetValue(CheckBoxListView.SelectedValuesPathProperty, value);
}
}
public CheckBoxListView()
: base()
{
InitializeComponent();
base.SelectionChanged += new SelectionChangedEventHandler(CheckBoxListView_SelectionChanged);
}
private void CheckBoxListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
List<object> values = new List<object>();
foreach (var item in SelectedItems)
{
if (string.IsNullOrWhiteSpace(SelectedValuesPath))
{
values.Add(item);
}
else
{
try
{
values.Add(item.GetType().GetProperty(SelectedValuesPath).GetValue(item, null));
}
catch { }
}
}
base.SetValue(CheckBoxListView.SelectedValuesProperty, values);
e.Handled = true;
}
}
}
My problem is that my binding only works one way right now. I'm having trouble trying to figure out how to implement my SelectedValues DependencyProperty so that I could Bind a Collection of values to it, and when the control is loaded, the CheckBoxes are checked with items that have values that correspond to the SelectedValues.
I've considered using the PropertyChangedCallBack event, but can't quite figure out how I could write that to achieve my goal.
I'm also unsure of how I find the correct ListViewItem to set it as Selected.
And lastly, if I can find the ListViewItem and set it to be Selected, won't that fire my SelectionChanged event each time I set a ListViewItem to be Selected?
Go through the below links.
Sync SelectedItems in a muliselect listbox with a collection in ViewModel
http://alexshed.spaces.live.com/blog/cns!71C72270309CE838!149.entry
http://blog.functionalfun.net/2009/02/how-to-databind-to-selecteditems.html

Continue WPF gridview alteration

Is it possible to continue the alteration styles in a gridview even when there are no items?
As you can see, after the last item, the pattern stops.
Yes, WPF provides a rather elegant way to implement this because its templating mechanism allows you to fill the unused area in a GridView with whatever you like.
All you need to do is modify the ListView template to paint the unused section of the with a VisualBrush that typically consists of two GridViewItems stacked vertically (in the general case it will be AlternationCount GridViewItems).
The only complexity is choosing which color to start with when painting the unused section of the ScrollViewer. This is calculated as Items.Count modulo AlternationCount. The solution is to create a simple Control that does this calculation and use it in our ListView template. For the sake of my explanation I will call the control "ContinueAlternation".
The ListView template which would be mostly the default template with a local:ContinueAlternation control added below the ScrollViewer using a DockPanel, like this:
<ControlTemplate TargetType="{x:Type ListView}">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="True">
<DockPanel>
<ScrollViewer DockPanel.Dock="Top"
Style="{DynamicResource {x:Static GridView.GridViewScrollViewerStyleKey}}"
Padding="{TemplateBinding Padding}">
<ItemsPresenter SnapsToDevicePixels="True" />
</ScrollViewer>
<local:ContinueAlternation
ItemContainerStyle="{TemplateBinding ItemContainerStyle}"
AlternationCount="{TemplateBinding AlternationCount}"
ItemsCount="{Binding Items.Count,
RelativeSource={RelativeSource TemplatedParent}}" />
</DockPanel>
</Border>
</ControlTemplate>
The ContinueAlternation control will be displayed as a Rectangle painted with a tiled VisualBrush containing an ItemsControl that shows dummy rows, as follows:
<ControlTemplate TargetType="{x:Type local:ContinueAlternation}">
<Rectangle>
<Rectangle.Fill>
<VisualBrush TileMode="Tile" Stretch="None"
ViewPortUnits="Absolute"
ViewPort="{TemplateBinding ViewportSize}">
<ItemsControl x:Name="PART_ItemsControl"
ItemsSource="{Binding}" />
</VisualBrush>
</Rectangle.Fill>
</Rectangle>
</ControlTemplate>
The DataContext here will be an array of dummy ListViewItem generated in code-behind from the given AlternationCount and ItemsCount:
public class ContinueAlternation
{
public Style ItemsContainerStyle ... // Declare as DependencyProperty using propdp snippet
public int AlternationCount ... // Declare as DependencyProperty using propdp snippet
public int ItemsCount ... // Declare as DependencyProperty using propdp snippet
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if(e.Property==ItemsContainerStyleProperty ||
e.Property==AlternationCountProperty ||
e.Property==ItemsCountProperty)
{
// Here is where we build the items for display
DataContext =
from index in Enumerable.Range(ItemsCount,
ItemsCount + AlternationCount)
select BuildItem( index % AlternationCount);
}
}
ListViewItem BuildItem(int alternationIndex)
{
var item = new ListViewItem { Style = ItemsContainerStyle };
ItemsControl.SetAlternationIndex(item, alternationIndex);
return item;
}
protected override Size MeasureOverride(Size desiredSize)
{
var ic = (ItemsControl)GetTemplateChild("PART_ItemsControl");
ic.Width = desiredSize.Width;
Size result = base.MeasureOverride(desiredSize);
ViewportSize = new Size(ic.DesiredSize);
return result;
}
public Size ViewportSize ... // Declare as DependencyProperty using propdp snippet
}
Note that this same code could be written with PropertyChangedCallback instead of OnPropertyChanged.
You also need to do something to make sure the blank rows are the desired height. The easiest way to do this is to set either MinHeight or Content in your ItemsContainerStyle. Alternatively ContinueAlternation could set the height when it constructs each ListViewItem.
I typed all this code off the top of my head, but it is similar to code I've written and used before so it ought to work basically as-is.

Resources