Berryl
Berryl

Reputation: 12833

wpf casting of generic items

I have a view model that currently looks like below. The TCm type is a domain model type, ie TelephoneNumber. And there are subclassed PcmDetailVms (ie, TelephoneNumberPcmDetailVm). I want to use these properties for a DataGrid that deals the specific TCm (ie, TelephoneNumber).

While it isn't horrible at all to subclass it and do the casting I am wondering if there is any built-in xaml features that would do the casting. Or if it would be more idiomatic to use a converter?

Cheers,
Berryl

view model

public class PcmShellVm<TCm> : ... where TCm : ContactMechanism
{
    public ObservableCollection<PcmDetailVm> DetailVms { get; protected set; }

    public PcmDetailVm SelectedVm {
        get { return _collectionView.CurrentItem as PcmDetailVm; } 
        set { _collectionView.MoveCurrentTo(value); }
    }
}

subclassed vm

public class TelecomNumberPcmShellVm : PcmShellVm<TelecomNumber>
{
    ...

    public IEnumerable<TelecomPcmDetailVm> CastedDetailVms { get { return DetailVms.Cast<TelecomPcmDetailVm>(); } }

    public TelecomPcmDetailVm CastedSelectedVm 
    {
        get { return (TelecomPcmDetailVm) SelectedVm; } 
        set { SelectedVm = value; }
    }
}

EDIT

So Andrei had the right answer to the question I did not ask very clearly above. I had incorrectly assumed I needed to cast my items somewhere in xaml, as I would need to do in code.

The binding engine must be using reflection to discover properties by name however, making the cast unnecessary.

Upvotes: 2

Views: 1327

Answers (1)

Andrei R&#238;nea
Andrei R&#238;nea

Reputation: 20780

I am not sure I understood the question correctly but I think specific DataTemplates for each type would be an idea.

View :

<Window x:Class="WpfApplication18.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:WpfApplication18="clr-namespace:WpfApplication18"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>
            <DataTemplate DataType="{x:Type WpfApplication18:PersonsVM}">
                <DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
                    <DataGrid.Columns>
                        <DataGridTextColumn Binding="{Binding FirstName}" Header="First name"/>
                        <DataGridTextColumn Binding="{Binding LastName}" Header="Last name"/>
                    </DataGrid.Columns>
                </DataGrid>
            </DataTemplate>
            <DataTemplate DataType="{x:Type WpfApplication18:SpecificPersonsVM}">
                <DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
                    <DataGrid.Columns>
                        <DataGridTextColumn Binding="{Binding LastName}" Header="Last name"/>
                        <DataGridTextColumn Binding="{Binding FirstName}" Header="First name"/>
                        <DataGridTextColumn Binding="{Binding Age}" Header="Age"/>
                    </DataGrid.Columns>
                </DataGrid>
            </DataTemplate>
        </Grid.Resources>

        <ContentControl Content="{Binding}"/>
    </Grid>
</Window>

ViewModels :

using System.Collections.ObjectModel;

namespace WpfApplication18
{
    public class PersonVM
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

    public class SpecificPersonVM : PersonVM
    {
        public byte Age { get; set; }
    }

    public class PersonsVM
    {
        public ObservableCollection<PersonVM> Items { get; private set; }

        public PersonsVM()
        {
            Items = new ObservableCollection<PersonVM>();
        }
    }

    public class SpecificPersonsVM
    {
        public ObservableCollection<SpecificPersonVM> Items { get; private set; }

        public SpecificPersonsVM()
        {
            Items = new ObservableCollection<SpecificPersonVM>();
        }
    }
}

Setupcode (code behind, yeah...)

namespace WpfApplication18
{
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();

            //SetupPersonsVM();
            SetupSpecificPersonsVM();
        }

        private void SetupSpecificPersonsVM()
        {
            var vm = new SpecificPersonsVM();
            vm.Items.Add(new SpecificPersonVM { FirstName = "Johny", LastName = "Bravo", Age = 17 });
            vm.Items.Add(new SpecificPersonVM { FirstName = "Dude", LastName = "Gray", Age = 22 });
            vm.Items.Add(new SpecificPersonVM { FirstName = "Scott", LastName = "Thomas", Age = 34 });
            DataContext = vm;
        }

        private void SetupPersonsVM()
        {
            var vm = new PersonsVM();
            vm.Items.Add(new PersonVM { FirstName = "John", LastName = "Scott" });
            vm.Items.Add(new PersonVM { FirstName = "Matthew", LastName = "Johnson" });
            DataContext = vm;
        }
    }
}

You can serve the view different VMs and it will render different things based on this. In my example there is a DataGrid for both VM types but it could have been more different.

Upvotes: 3

Related Questions