WPF - Как реализовать DataTemplate со сложной логикой? - PullRequest
0 голосов
/ 25 декабря 2018

В настоящее время я переносю свое приложение из WinForms в WPF .Поскольку я новичок в WPF, я застрял в создании DataTemplates для своих элементов treeView.На снимке экрана показано, как выглядит мое дерево в версии WinForms , и мне нужно получить результат закрытия в WPF .

(дерево My WinForms) Как видите, логика моего DataTemplate должна учитывать следующие факторы:

  1. Тип узла / определяет, какая иконка и комбинация полей будут отображаться для конкретного элемента (узла).Приложение имеет около 7-8 типов узлов.Тип хранится в поле отдельного узла.
  2. Значения переменных / Мне нужно заменить на текст, если ноль, и т. Д.
  3. Числовые значения переменных / например: установить серый цвет, если ноль, и т. Д.
  4. Другие свойства / например: добавление текстовых блоков в зависимости от логических полей.
  5. И так далее ...

Все эти факторы приводят к огромному количеству возможных комбинаций параметров элемента.

Также я использую DevComponents WPF DotNetBar AdvTree для разделения свойств элемента на столбцы.Я предполагаю, что должен создать «подшаблоны» для разных наборов полей и составить из них весь шаблон данных для каждого столбца.Я узнал о триггерах и должен сказать, что реализация моей логики с триггерами в любом случае сделает мои подшаблоны огромными.

(Текущее состояние моего WPF-дерева) Итак, вот мои вопросы:

  1. Существуют ли способы динамического создания сложных шаблонов с кодом C # ( без создания необработанного XAML и загрузки его во время выполнения)?
  2. Может быть, я должен использовать совершенно другой способ (вместо использования DataTemplate)?В Winforms я просто использовал режим OwnerDraw, поэтому задача была НАМНОГО проще, чем в WPF: (
  3. А как отобразить вложенные свойства внутри шаблона? Например: Item.Prop.Subprop1.Subprop2. Targetprop .

PS: английский не мой родной язык, извините за ваши глаза.

1 Ответ

0 голосов
/ 25 декабря 2018

1) Ответ - да.Например, если вы хотите определить шаблон в вашем окне для простой строки

public MainWindow()
{
    InitializeComponent();

    DataTemplate template = new DataTemplate(typeof(string));
    FrameworkElementFactory borderFactory = new FrameworkElementFactory(typeof(Border));
    borderFactory.SetValue(Border.PaddingProperty, new Thickness(1));
    borderFactory.SetValue(Border.BorderThicknessProperty, new Thickness(1));
    borderFactory.SetValue(Border.BorderBrushProperty, Brushes.Red);

    FrameworkElementFactory textFactory = new FrameworkElementFactory(typeof(TextBlock));
    textFactory.SetBinding(TextBlock.TextProperty, new Binding
    {
        Mode = BindingMode.OneWay
    });
    borderFactory.AppendChild(textFactory);
    template.VisualTree = borderFactory;

    myControl.ContentTemplate = template;
}

И в окне вы просто поместите что-то вроде

<ContentControl x:Name="myControl" Content="Test text" Margin="10"/>

Ваш элемент управления контентом будет отображать строкуокружен красной каймой.Но, как вы видите, очень сложно определить ваши шаблоны таким образом.Единственный сценарий, в котором я мог бы представить себе такой подход, это для каких-то процедурно сгенерированных шаблонов.

Другой способ - сгенерировать строку для шаблона и затем загрузить ее с помощью XamlReader:

string xaml = "<Ellipse Name=\"EllipseAdded\" Width=\"300.5\" Height=\"200\" 
Fill=\"Red\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"/>";
object ellipse = XamlReader.Load(xaml);

2) Я не вижу необходимости создавать шаблоны в коде позади.Например, для такой структуры данных:

public class User
{
    public string Name { get; set; }
    public User Friend { get; set; }
}
public class RootNode
{
    public string Title { get; set; }
    public User User { get; set; }
    public List<Node> Nodes { get; set; }
}
public class Node
{
    public string Title { get; set; }
    public List<SubNode> SubNodes { get; set; }
}
public class SubNode
{
    public string Title { get; set; }
}

Вы можете определить этот тип шаблона:

<Window 
        ...
        Title="MainWindow" Height="350" Width="525" >

    <Window.Resources>
        <HierarchicalDataTemplate DataType="{x:Type local:RootNode}" ItemsSource="{Binding Nodes}">
            <StackPanel x:Name="spContainer" Orientation="Horizontal">
                <TextBlock Text="{Binding Title}"/>
                <TextBlock Text="{Binding User.Friend.Friend.Name}"/>
            </StackPanel>
            <HierarchicalDataTemplate.Triggers>
                <DataTrigger Binding="{Binding User}" Value="{x:Null}">
                    <Setter TargetName="spContainer" Property="Background" Value="Yellow"/>
                </DataTrigger>
            </HierarchicalDataTemplate.Triggers>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate DataType="{x:Type local:Node}" ItemsSource="{Binding SubNodes}">
            <TextBlock Text="{Binding Title}"/>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate DataType="{x:Type local:SubNode}" ItemsSource="{Binding Nodes}">
            <TextBlock Text="{Binding Title}"/>
        </HierarchicalDataTemplate>

    </Window.Resources>

    <Grid>

        <TreeView ItemsSource="{Binding RootNodes}"/>

    </Grid>
</Window>

Как видите, вы можете определить шаблон по типу данных, вы можететакже используйте триггеры для изменения поведения в определенных случаях, вы также можете использовать сом-конвертеры привязки ...

3) Вы можете привязать к вложенным свойствам так же, как к обычным:

<TextBlock Text="{Binding User.Friend.Friend.Name}"/>

Однако в некоторых случаях может произойти сбой более двух привязок уровня (не удается разрешить или обновить при изменении свойства, ...)

...