तो मेरे पास कक्षाओं का एक समूह है जो कुछ बेस क्लास से प्राप्त होते हैं। मेरे पास कक्षाएं (कलेक्टर) हैं जिनके पास ऐसी विधियां हैं जो इन वर्गों के संग्रह लौटाती हैं। मेरे पास एक TabControl भी है जहां प्रत्येक टैब का कस्टम नियंत्रण होता है जिसमें एक DataGrid होता है। इन कस्टम नियंत्रणों के लिए एक ViewModel है। व्यूमोडेल में मेरे पास बेस क्लास तत्वों का संग्रह है जो कलेक्टरों द्वारा लौटाए जाते हैं। मैं इन संग्रहों में DataGrid को बांधना चाहता हूं और स्वचालित रूप से कॉलम जेनरेट करना चाहता हूं, लेकिन व्युत्पन्न कक्षाओं में अलग-अलग गुण होते हैं और बेस क्लास में कोई गुण नहीं होता है जिसे दिखाया जाना चाहिए।

internal class ElementsInfoViewModel : INotifyPropertyChanged
{
    private readonly ElementScaner _elementScaner;

    public ElementsInfoViewModel(ElementScaner elementScaner)
    {
        _elementScaner = elementScaner;
    }

    public ReadOnlyCollection<SystemElement> ShownElements => _elementScaner.Elements;
}

<UserControl x:Class="SysSpy.Desktop.Controls.ElementsInfoTabItemContent"
             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:toolkit="http://schemas.microsoft.com/wpf/2008/toolkit"
             xmlns:local="clr-namespace:SysSpy.Desktop.Controls"
             mc:Ignorable="d">
    <Grid>
        <toolkit:DataGrid ItemsSource="{Binding ShownElements}">
        </toolkit:DataGrid>
    </Grid>
</UserControl>

मैंने कोड से अनावश्यक सब कुछ हटा दिया।

मेरे दिमाग में यह विचार आता है कि किसी तरह आधार प्रकार के संग्रह को व्युत्पन्न प्रकार के संग्रह में डालना (संभवतः IValueConverter के साथ), लेकिन जैसा कि संग्रह प्रति सेकंड कई बार अपडेट किया जाता है, प्रतिबिंब इसके लिए खराब समाधान हो सकता है

यूपीडी

TabControl मुख्य vm . में कुछ संग्रह से आबद्ध है

 internal class MainViewModel : INotifyPropertyChanged
    {
        public MainViewModel()
        {
            Test = new ObservableCollection<ElementsInfoViewModel>();

            var certificatesCollector = new CertificatesCollector();
            var certificatesScaner = new ElementScaner(certificatesCollector, "Certificates");
            certificatesScaner.Scan();
            var certifVM = new ElementsInfoViewModel(certificatesScaner);

            Test.Add(certifVM);
        }

        public ObservableCollection<ElementsInfoViewModel> Test { get; set; 
}

    }

<Window x:Class="SysSpy.Desktop.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SysSpy.Desktop"
        xmlns:viewModel="clr-namespace:SysSpy.Desktop.ViewModels"
        xmlns:controls="clr-namespace:SysSpy.Desktop.Controls"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.Resources>
        <viewModel:MainViewModel x:Key="viewModelSource"/>
    </Window.Resources>

    <Window.DataContext>
        <Binding Source="{StaticResource viewModelSource}"/>
    </Window.DataContext>

    <DockPanel LastChildFill="True">
        <Grid>
            <TabControl ItemsSource="{Binding Test}">
                <TabControl.ItemTemplate>
                    <DataTemplate DataType="viewModel:ElementsInfoViewModel">
                        <controls:ElementsInfoTabItemHeader/>
                        
                    </DataTemplate>
                </TabControl.ItemTemplate>
                <TabControl.ContentTemplate>
                    <DataTemplate DataType="viewModel:ElementsInfoViewModel">
                        <controls:ElementsInfoTabItemContent/>
                        
                    </DataTemplate>
                </TabControl.ContentTemplate>
                
            </TabControl>
            
        </Grid>
    </DockPanel>
</Window>
1
serjou 4 नवम्बर 2020, 23:19

2 जवाब

सबसे बढ़िया उत्तर

मुझे नहीं पता कि आपको यही चाहिए, लेकिन मेरे पास एक और विकल्प है। चूंकि डेटाग्रिड के पास कॉलम को डायनेमिक रूप से बाध्य करने की कोई विधि नहीं है, आप इसे बढ़ा सकते हैं और इसे स्वयं बना सकते हैं।

उदाहरण के लिए इस तरह:

class MyDataGrid : DataGrid
{

    public MyDataGrid() : base()
    {
    }

    protected override void OnItemsSourceChanged(System.Collections.IEnumerable oldValue, System.Collections.IEnumerable newValue)
    {
        base.OnItemsSourceChanged(oldValue, newValue);
        if (newValue != null)
        {
            var enumerator = newValue.GetEnumerator();
            if (enumerator.MoveNext())
            {
                Columns.Clear();
                var firstElement = enumerator.Current;
                var actualType = firstElement.GetType();
                foreach (var prop in actualType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.CanRead))
                {
                    Columns.Add(new DataGridTextColumn
                    {
                        Header = prop.Name,
                        Binding = new Binding(prop.Name)
                    });
                }

            }
        }
    }
}

मैंने इसका परीक्षण करने के लिए एक साधारण परियोजना लिखी है और यह मेरे पर्यावरण में काम करती है।

पहली वस्तु प्रकार (व्यक्ति)

class Person 
{

    public string Name { get; set; }

    public string Surname { get; set; }

    public int Age { get; set; }
}

दूसरी वस्तु प्रकार (कार)

class Car 
{

    public string Name { get; set; }


    public int HP { get; set; }
}

वह तत्व जिसे आपने SystemElement कहा है (शीर्षक का उपयोग टैब हेडर के रूप में किया जाता है)

class SystemElement
{

    public SystemElement(string title, IList<object> elements)
    {
        Title = title;
        Elements = new ReadOnlyCollection<object>(elements);
    }


    public string Title { get; set; }


    public ReadOnlyCollection<object> Elements { get; }


}

ElementScanner जो दिखाने के लिए तत्वों को निकालता है। मेरे परीक्षण में सूची निर्माता में सेट है:

class ElementScanner
{

    public ElementScanner()
    {
        var data = new List<SystemElement>();

        data.Add(new SystemElement("People", new List<object>
        {
            new Person { Name = "John", Surname = "Doe", Age = 22},
            new Person { Name = "Lenny", Surname = "Pegasus", Age = 30},
            new Person { Name = "Duffy", Surname = "Duck", Age = 22}
        }));

        data.Add(new SystemElement("Cars", new List<object>
        {
            new Car { Name = "Mercedes", HP = 700 },
            new Car { Name = "Red Bull", HP = 650 },
            new Car { Name = "Ferrari", HP = 600 }
        }));

        Elements = new ReadOnlyCollection<SystemElement>(data);
    }

    public ReadOnlyCollection<SystemElement> Elements { get; }
}

अब हमारे पास व्यूमोडेल है। मेरे उदाहरण में मैंने INotifyPropertyChanged का उपयोग नहीं किया क्योंकि गुण कभी नहीं बदलेंगे (यह एक परीक्षण परियोजना है)।

class ElementsInfoViewModel
{

    public ElementsInfoViewModel()
    {
        var elementScanner = new ElementScanner();
        Elements = elementScanner.Elements;
    }

    public ReadOnlyCollection<SystemElement> Elements { get; }
}

अब व्यू साइड में जाएं। सबसे पहले हमारे पास UserControl है जो प्रत्येक टैब की सामग्री को दर्शाता है। तो यह MyDataGrid प्रकार का डेटाग्रिड दिखाएगा:

<UserControl x:Class="Stack.ElementInfoView"
             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:local="clr-namespace:Stack"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <local:MyDataGrid ItemsSource="{Binding Elements}" >
        </local:MyDataGrid>
    </Grid>
</UserControl>

अंत में हमारे पास मुख्य दृश्य है जो सभी को एक साथ मिला देगा:

<Window x:Class="Stack.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Stack"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:ElementsInfoViewModel />
    </Window.DataContext>
    <Grid>
        <TabControl ItemsSource="{Binding Elements}">
            <TabControl.ItemContainerStyle>
                <Style TargetType="TabItem">
                    <Setter Property="Header" Value="{Binding Title}" />
                </Style>
            </TabControl.ItemContainerStyle>
            <TabControl.ContentTemplate>
                <DataTemplate DataType="local:ElementsInfoViewModel">
                    <local:ElementInfoView></local:ElementInfoView>
                </DataTemplate>
            </TabControl.ContentTemplate>
        </TabControl>
    </Grid>
</Window>

हमारे पास यही है (और मुझे लगता है कि यह वही है जो आप चाहते हैं):

Main Window

1
PiGi78 7 नवम्बर 2020, 02:14

मुझे नहीं पता कि यह काम करता है, लेकिन क्या आपने जेनरिक के साथ कोशिश की है? मेरा मतलब कुछ इस तरह है:

internal class ElementsInfoViewModel<T> : INotifyPropertyChanged
               where T : SystemElement
{

    public ElementsInfoViewModel(ElementScaner elementScaner)
    {
        var list = new List<T>();
        foreach (var el in elementScanner.Elements)
        {
           list.Add(el as T);
        }
        ShownElements = new ReadOnlyCollection<T>(list);
    }

    public ReadOnlyCollection<T> ShownElements { get; }
}

मैं इसे ब्राउज़र में लिखता हूं, इसलिए मेरा कोड गलत हो सकता है, लेकिन विचार करें। यदि यह अच्छा हो सकता है, तो सबसे अच्छा होगा ElementScanner का सामान्य संस्करण (कुछ ऐसा ElementScanner< T > )।

हमें बताएं कि क्या यह काम करेगा

1
PiGi78 4 नवम्बर 2020, 23:31