Question

I wanted to avoid scrolling ListView's Header, so I've created custom controll for List, which consists of grid with two rows (first of them is header and in second I could place the ListView, but I've successfully binded header with controll, but I can't do the same with data.

My ListViewCustomControl class:

   public sealed class ListViewCustomControl : Control
    {
        public static readonly DependencyProperty HeaderProperty =
            DependencyProperty.Register(
            "Header", typeof(string), typeof(ListViewCustomControl),
            new PropertyMetadata("custom_template_row1"));

        public static readonly DependencyProperty ElementsSourceProperty =
            DependencyProperty.Register(
            "ElementsSource", typeof(IObservable<Type>), typeof(ListViewCustomControl),
            null);


        public ListViewCustomControl()
        {
            DefaultStyleKey = typeof(ListViewCustomControl);
        }

        public string Header
        {
            get { return (string)GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }

        public IObservable<Type> ElementsSource
        {
            get { return (IObservable<Type>)GetValue(ElementsSourceProperty); }
            set { SetValue(ElementsSourceProperty, value); }
        }
    }

Style for my control:

<Style TargetType="local:ListViewCustomControl">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:ListViewCustomControl">
                <Grid Background="BlueViolet">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"></RowDefinition>
                        <RowDefinition Height="*"></RowDefinition>
                    </Grid.RowDefinitions>
                    <TextBlock Text="{TemplateBinding Header}"></TextBlock>
                    <ListView Grid.Row="1"
                              ItemsSource="{TemplateBinding ElementsSource}">
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding TypeVar}"></TextBlock>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Sample of using in xaml file:

        <local:ListViewCustomControl
            Header="yolo" ElementsSource="{Binding data}">
        </local:ListViewCustomControl>

sent data and Type definition

private void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
    var data = new List<Type>();
    for (var i = 0; i < 123; i++) data.Add(new Type(i));
    defaultViewModel["data"] = data;
}
public class Type
{
    public int TypeVar { get; set; }

    public Type(int i)
    {
        TypeVar = i;
    }
}

As I've mentioned on the beginning. Header property works well, but I can't make data binding working well too...

update

I've noticed in debugger that SetValue function from XamlTypeInfo.g.cs has been invoked once, only to set Header value.

update

I've changed IObservable to IList and what's more important default value of ElementsSourceProperty and it works for default value, but I still can't change this value...

    public static readonly DependencyProperty ElementsSourceProperty =
        DependencyProperty.Register(
        "ElementsSource", typeof(IList<Type>), typeof(ListViewCustomControl),
        new PropertyMetadata(
            new List<Type>(3)
            {
                new Type(4), new Type(5), new Type(6)
            }));
Was it helpful?

Solution

Try change type of ElementsSource:

IObservable<>

to:

IList<>

like this:

public static readonly DependencyProperty ElementsSourceProperty =
        DependencyProperty.Register(
        "ElementsSource", typeof(IList<Type>), typeof(ListViewCustomControl),
        null);

public IList<Type> ElementsSource
{
    get { return (IList<Type>)GetValue(ElementsSourceProperty); }
    set { SetValue(ElementsSourceProperty, value); }
}

For test, try set for ListView name and write this line in code-behind:

MyListView.ElementsSource = data;

Version with Binding

In this case, you must specify the DataContext, which would you want us to list:

XAML

<local:ListViewCustomControl Header="yolo"
                             ElementsSource="{Binding TestList}" />

Code-behind

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        var testData = new TestData();
        testData.TestList = new List<Type>();

        for (var i = 0; i < 123; i++)
            testData.TestList.Add(new Type(i));

        this.DataContext = testData;
    }
}

public class TestData 
{
    public List<Type> TestList
    {
        get; 
        set; 
    }
}

Some notes

  • You can choose the type of IEnumerable, because it is the base class for all collections.

  • When you create a dependency property you can skip PropertyMetadata, in this case, the system will automatically assign a default value for this type.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top