简体   繁体   中英

Custom ListView DataTemplate Binding / XAML Issue

I wanted to create a friendlist for my WPF MVVM (Light Toolkit) Application, which will get filled from database generated User Objects (in ObservableCollection or List?) and should be displayed as following:

在此处输入图片说明

I'm using MahApps Metro and would use Tiles in a WrapPanel for each alphabetic Letter. But now I can't figure out how I correctly build the DataTemplate for the ListView.

So if my Code is totally wrong, how could I correctly create the View (from image above) in XAML with DataBindings as you can see in the example code? Would be a GridView better?

My XAML (FriendlistView):

<UserControl
<!-- Stuff -->
DataContext="{Binding Friendlist, Source={StaticResource Locator}}">

<Grid Background="White">
<ListView ItemsSource="{Binding Path=FriendsCompleteList}">
    <ListView.View>
        <GridView>
            <GridViewColumn>
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <ItemsControl ItemsSource="{Binding Path=Friends}">
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <StackPanel Orientation="Vertical">
                                        <TextBlock Text="{Binding Path=Letter}"/>
                                        <WrapPanel Orientation="Horizontal"/>
                                    </StackPanel>
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <StackPanel>
                                        <controls:Tile Title="{Binding Path=Username}"
                                                       TiltFactor="2"
                                                       Height="100" Width="100">
                                            <Image Height="40" Width="40"/>
                                        </controls:Tile>
                                    </StackPanel>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
<!-- Just Closing Tags -->

My FriendlistViewModel as DataContext:

public class FriendlistViewModel : ViewModelBase
{
    private List<FriendlistPart> _friendsCompleteList;

    public FriendlistViewModel(List<FriendlistPart> friendsCompleteList)
    {
        FriendsCompleteList = friendsCompleteList;
    }

    public List<FriendlistPart> FriendsCompleteList
    {
        get { return _friendsCompleteList; }
        set { Set(ref _friendsCompleteList, value); }
    }
}

public class FriendlistPart
{
    public string Letter { get; set; }
    public IList<User> Friends { get; set; }
}

And my MainViewModel to Display it with Test Data:

public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        var userList= new List<User>{
            new User { Username = "test" },
            new User { Username = "test1" },
            new User { Username = "test2" }
        };

        var friendlistCompleteList= new List<FriendlistPart>
        {
            new FriendlistPart { Friends = userList, Letter = "A" },
            new FriendlistPart { Friends = userList, Letter = "B" },
            new FriendlistPart { Friends = userList, Letter = "C" }
        };

        CurrentPageViewModel = new FriendlistViewModel(friendlistCompleteList);
    }
}

Edit: Tried Example from @Ilan with Mvvm Light and tweaked it:

My projectstructure is like this:

  • Model
    • User
    • FriendlistSection
  • View
    • FriendlistView
  • ViewModel
    • FriendlistViewModel
    • MainViewModel
    • ViewModelLocator

I created the ListView in FriendlistView:

<UserControl x:Class="Messenger4u.View.FriendlistView"
         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"
         xmlns:model="clr-namespace:Messenger4u.Model"
         xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
         xmlns:messenger4U="clr-namespace:Messenger4u"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300"
         DataContext="{Binding Main, Source={StaticResource Locator}}">

<Grid x:Name="LayoutRoot" DataContext="{Binding CurrentPageViewModel}">
    <ListView x:Name="ItemsPresentingListView" Margin="0, -5, 0, 0" BorderThickness="0" ItemsSource="{Binding Path=FriendlistComplete}" >
        <i:Interaction.Behaviors>
            <messenger4U:IgnoreMouseWheelBehavior />
        </i:Interaction.Behaviors>
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="ContentTemplate">
                    <Setter.Value>
                        <DataTemplate DataType="{x:Type model:FriendlistSection}">
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition/>
                                    <RowDefinition/>
                                </Grid.RowDefinitions>
                                <TextBlock Grid.Row="0" Margin="0, 5, 0, 5" Text="{Binding Path=Letter}" FontSize="15"/>
                                <Rectangle Grid.Row="0" VerticalAlignment="Bottom" Fill="Black" Height="1" HorizontalAlignment="Stretch"/>
                                <ListBox Grid.Row="1" Margin="-2, 0, 0, 0" ItemsSource="{Binding Friends}">
                                    <ItemsControl.ItemsPanel>
                                        <ItemsPanelTemplate>
                                            <WrapPanel Orientation="Horizontal" Width="{Binding ActualWidth, ElementName=LayoutRoot}"/>
                                        </ItemsPanelTemplate>
                                    </ItemsControl.ItemsPanel>
                                    <ItemsControl.ItemTemplate>
                                        <DataTemplate>
                                            <StackPanel>
                                                <controls:Tile Title="{Binding Username}"
                                                               TiltFactor="2"
                                                               Height="100" Width="100"
                                                               Margin="8, 15, 15, 15"
                                                               Background="DodgerBlue">
                                                    <Image Height="40" Width="40" Source="../Skins/Images/user.jpg"/>
                                                </controls:Tile>
                                            </StackPanel>
                                        </DataTemplate>
                                    </ItemsControl.ItemTemplate>
                                </ListBox>
                            </Grid>
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListView.ItemContainerStyle>
    </ListView>
</Grid>

FriendlistViewModel:

public class FriendlistViewModel : ViewModelBase
{
    private ObservableCollection<FriendlistSection> _friendlistComplete;

    public FriendlistViewModel(List<FriendlistSection> friendlist)
    {
        FriendlistComplete = new ObservableCollection<FriendlistSection>(friendlist);
    }

    public ObservableCollection<FriendlistSection> FriendlistComplete
    {
        get { return _friendlistComplete; }
        set { Set(ref _friendlistComplete, value); }
    }
}

FriendlistSection:

public class FriendlistSection : ObservableObject
{
    private string _letter;
    private ObservableCollection<User> _friends;

    public string Letter
    {
        get { return _letter; }
        set { Set(ref _letter, value); }
    }

    public ObservableCollection<User> Friends
    {
        get { return _friends; }
        set { Set(ref _friends, value); }
    }
}

User:

public class User : ObservableObject
{
    private string _username;

    public string Username
    {
        get { return _username; }
        set { Set(ref _username, value); }
    }

MainViewModel:

public MainViewModel()
    {
        var userList = new ObservableCollection<User>
        {
            new User {Username = "test"},
            new User {Username = "test1"},
            new User {Username = "test2"}
        };

        var userList1 = new ObservableCollection<User>
        {
            new User {Username = "test3"},
            new User {Username = "test4"},
            new User {Username = "test2"},
            new User {Username = "test5"},
            new User {Username = "test6"},
            new User {Username = "test7"},
            new User {Username = "test8"},
        };

        var userList2 = new ObservableCollection<User>
        {
            new User {Username = "test9"},
            new User {Username = "test10"},
        };

        var friendlistCompleteList = new List<FriendlistSection>
        {
            new FriendlistSection {Friends = userList, Letter = "A"},
            new FriendlistSection {Friends = userList1, Letter = "B"},
            new FriendlistSection {Friends = userList2, Letter = "C"}
        };
        // Can use var, but here was my mistake to create the 
        // FriendlistViewModel as ViewModelBase
        FriendlistViewModel friendlistViewModel = new FriendlistViewModel(friendlistCompleteList);
        CurrentPageViewModel = friendlistViewModel ;
    }

    // Bound to ContentControl in MainWindow
    public ViewModelBase CurrentPageViewModel
    {
        get { return _currentPageViewModel; }
        set { Set(ref _currentPageViewModel, value); }
    }

ViewModelLocator:

static ViewModelLocator()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

        SimpleIoc.Default.Register<User>();
        SimpleIoc.Default.Register<FriendlistSection>();

        SimpleIoc.Default.Register<MainViewModel>();
        SimpleIoc.Default.Register<FriendlistViewModel>();
    }

    public MainViewModel Main => ServiceLocator.Current.GetInstance<MainViewModel>();
    public FriendlistViewModel Friendlist => ServiceLocator.Current.GetInstance<FriendlistViewModel>();

Now it works fine, added the IgnoreMouseWheelBehaivior, because it gets handled by a ScrollViewer and set the WrapPanel Width to ActualWidth from the Grid that gets sized trough the ScrollViewer in MainWindow.

Thanks again to @Ilan!

Now it looks like this:

在此处输入图片说明

please try the next:

Xaml code:

<Window x:Class="ListViewDataTemplateContainingListViewAndMore.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:listViewDataTemplateContainingListViewAndMore="clr-namespace:ListViewDataTemplateContainingListViewAndMore"
    Title="MainWindow" Height="350" Width="525" x:Name="This">
<Window.DataContext>
    <listViewDataTemplateContainingListViewAndMore:MainViewModel/>
</Window.DataContext>
<Grid x:Name="LayoutRoot" DataContext="{Binding CurrentPageViewModel}">
    <ListView x:Name="ItemsPresentingListView" ItemsSource="{Binding Path=FriendsCompleteList}">
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="ContentTemplate">
                    <Setter.Value>
                        <DataTemplate DataType="{x:Type listViewDataTemplateContainingListViewAndMore:FriendlistPart}">
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition ></RowDefinition>
                                    <RowDefinition ></RowDefinition>
                                </Grid.RowDefinitions>
                                <TextBlock Grid.Row="0" Text="{Binding Path=Letter}" FontWeight="Bold" FontSize="15"/>
                                <Rectangle Grid.Row="0" VerticalAlignment="Bottom" Fill="Black" Height="3" HorizontalAlignment="Stretch"></Rectangle>
                                <ListBox Grid.Row="1" ItemsSource="{Binding Friends}" BorderBrush="#00FFFFFF">
                                    <ItemsControl.ItemsPanel>
                                        <ItemsPanelTemplate>
                                            <WrapPanel Orientation="Horizontal" Width="500"/>
                                        </ItemsPanelTemplate>
                                    </ItemsControl.ItemsPanel>
                                    <ItemsControl.ItemTemplate>
                                        <DataTemplate>
                                            <StackPanel>
                                                <Grid>
                                                    <Rectangle Width="120" Height="120" Fill="Blue"></Rectangle>
                                                    <Ellipse Width="90" Height="90" Fill="Tomato"></Ellipse>
                                                </Grid>
                                                <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">
                                                    <Run Text="Name: "></Run>
                                                    <Run Text="{Binding Username}"></Run>
                                                </TextBlock>
                                            </StackPanel>
                                        </DataTemplate>
                                    </ItemsControl.ItemTemplate>
                                </ListBox></Grid>
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListView.ItemContainerStyle>
    </ListView>
</Grid>

  1. You don't need the GridView (you don't have any table).
  2. Use the ListBox instead the ItemsControl to allow selection.
  3. Set the WrapPanel width to see the wrapping working (optional).
  4. I don't have Tile control, thus I've used rectangle to simulate it.
  5. Please take in attention that a Grid (named "LayoutRoot") provides the DataContext for the ListView (named "ItemsPresentingListView").

Here are a modified view models (please take in account the next article )

public class FriendlistViewModel : BaseObservableObject
{
    private ObservableCollection<FriendlistPart> _friendsCompleteList;

    public FriendlistViewModel(List<FriendlistPart> friendsCompleteList)
    {
        FriendsCompleteList = new ObservableCollection<FriendlistPart>(friendsCompleteList);
    }

    public ObservableCollection<FriendlistPart> FriendsCompleteList
    {
        get { return _friendsCompleteList; }
        set { Set(ref _friendsCompleteList, value); }
    }
}

public class FriendlistPart:BaseObservableObject
{
    private string _letter;
    private ObservableCollection<User> _friends;

    public string Letter
    {
        get { return _letter; }
        set
        {
            _letter = value;
            OnPropertyChanged();
        }
    }

    public ObservableCollection<User> Friends
    {
        get { return _friends; }
        set
        {
            _friends = value;
            OnPropertyChanged();
        }
    }
}

public class User:BaseObservableObject
{
    private string _username;

    public string Username
    {
        get { return _username; }
        set
        {
            _username = value;
            OnPropertyChanged();
        }
    }
}

public class MainViewModel : BaseObservableObject
{
    private FriendlistViewModel _currentPageViewModel;

    public MainViewModel()
    {
        var userList = new ObservableCollection<User>
        {
            new User {Username = "test"},
            new User {Username = "test1"},
            new User {Username = "test2"}
        };

        var userList1 = new ObservableCollection<User>
        {
            new User {Username = "test3"},
            new User {Username = "test4"},
            new User {Username = "test2"},
            new User {Username = "test5"},
            new User {Username = "test6"},
            new User {Username = "test7"},
            new User {Username = "test8"},
        };

        var userList2 = new ObservableCollection<User>
        {
            new User {Username = "test9"},
            new User {Username = "test10"},
        };

        var friendlistCompleteList = new List<FriendlistPart>
        {
            new FriendlistPart {Friends = userList, Letter = "A"},
            new FriendlistPart {Friends = userList1, Letter = "B"},
            new FriendlistPart {Friends = userList2, Letter = "C"}
        };

        CurrentPageViewModel = new FriendlistViewModel(friendlistCompleteList);
    }

    public FriendlistViewModel CurrentPageViewModel
    {
        get { return _currentPageViewModel; }
        set
        {
            _currentPageViewModel = value;
            OnPropertyChanged();
        }
    }
}

How it looks like: 这里

Regards.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM