[WPF] ControlTemplate 적용

ControlTemplate이 적용되는 원리를 잘 모르겠습니다.
DevExpress에 문의해서 받은 샘플인데…이 샘플과 똑같이 수행해도 제 소스에서는 선택 시 테두리가 적용되지 않네요.

<Window
    x:Class="DXSample.NetCore.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:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
    xmlns:dxgt="http://schemas.devexpress.com/winfx/2008/xaml/grid/themekeys"
    xmlns:local="clr-namespace:DXSample.NetCore"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="800"
    Height="450"
    UseLayoutRounding="True"
    mc:Ignorable="d">

    <Window.Resources>
        <ControlTemplate x:Key="{dxgt:GridCardThemeKey ResourceKey=ContainerTemplate, ThemeName=Office2019Colorful}" TargetType="{x:Type ContentControl}">
            <Grid x:Name="Root" Background="Transparent">
                <Border
                    x:Name="IsDefault"
                    Background="#FFF8F8F8"
                    BorderBrush="#FFABABAB"
                    BorderThickness="1">
                    <ContentPresenter />
                </Border>
            </Grid>
            <ControlTemplate.Triggers>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsMouseOver" Value="True" />
                        <Condition Property="dxg:GridViewBase.IsFocusedRow" Value="False" />
                    </MultiTrigger.Conditions>
                </MultiTrigger>
                <DataTrigger Binding="{Binding Path=(dxg:RowData.RowData).SelectionState, RelativeSource={RelativeSource TemplatedParent}}" Value="Focused">
                    <Setter TargetName="IsDefault" Property="BorderBrush" Value="Red" />
                </DataTrigger>
                <DataTrigger Binding="{Binding Path=(dxg:RowData.RowData).SelectionState, RelativeSource={RelativeSource TemplatedParent}}" Value="Selected">
                    <Setter TargetName="IsDefault" Property="BorderBrush" Value="Red" />
                </DataTrigger>
                <Trigger Property="dxg:GridViewBase.IsFocusedRow" Value="True" />
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </Window.Resources>

    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>

    <DockPanel>
        <Button Command="{Binding SelectCommand}" DockPanel.Dock="Bottom">
            Select
        </Button>
        <dxg:GridControl
            AutoGenerateColumns="AddNew"
            ItemsSource="{Binding Items}"
            SelectedItems="{Binding SelectedItems}"
            SelectionMode="Row">
            <dxg:GridControl.View>
                <dxg:CardView />
            </dxg:GridControl.View>
        </dxg:GridControl>
    </DockPanel>

</Window>
using System.Windows;
using System.Collections.ObjectModel;
using DevExpress.Mvvm;
using DevExpress.Mvvm.DataAnnotations;

namespace DXSample.NetCore {
    public partial class MainWindow : Window {
        public MainWindow() {
            InitializeComponent();
        }
    }
    public class MainViewModel : ViewModelBase {
        public ObservableCollection<Item> Items { get => GetValue<ObservableCollection<Item>>(); set => SetValue(value); }
        public ObservableCollection<Item> SelectedItems { get => GetValue<ObservableCollection<Item>>(); set => SetValue(value); }
        public MainViewModel() {
            Items = new ObservableCollection<Item>();
            for (int i = 0; i < 100; i++) {
                var item = new Item { Id = i, Name = string.Format("Name_{0}", i) };
                Items.Add(item);
            }
            SelectedItems = new ObservableCollection<Item>();
        }
        [Command]
        public void Select() {
            SelectedItems.Clear();
            for (int i = 0; i < 10; i++)
                SelectedItems.Add(Items[i]);
        }
    }
    public class Item {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

소스첨부가 되지 않아 복붙으로 남깁니다. 위의 소스가 샘플의 전부이므로, MainWindow.xaml하고 코드비하인드에 붙여넣으시면 됩니다.

실행하시고 Visual Tree 켜보시면 CardView의 Item으로 ControlTemplate으로 정의한 요소들이 출력되는 것을 볼 수 있습니다.

이것은 DevExpress의 기술일까요? WPF에서 모든 컨트롤은 ContentControl, ItemsControl 둘 중하나를 상속받아 구현된 다는 것은 알고 있습니다. 왜 TargetType으로 ContentControl을 지정했는데 CardView의 Item에만 생기는 것일까요?

그리고 혹시…제 PC에 이걸 왜 적용해도 안되는지 혹시 봐주실 분이 계시다면 LiveShare도 요청드립니다…ㅎㅎ

감사합니다!

1개의 좋아요

@Vincent 샘플은 잘 동작하는데 옮기면 적용이 안된다는 말씀이신가요?

집에 가서 한번 해볼게요~

네 그리고 Border, Panel 빼고는 모두 Control을 상속받는데
ContentControl 또는 ItemsControl에서 virutal로 제공하는 Template 셀렉팅 관련 메서드가 있어서
DevExpress도 이를 Override 받아 처리하는 부분이 있을수도 있어요. 아마 테마? 관련해서 무언가 처리할 수도 있고… 설명이 좀 부족하네요. :joy:

집에가서 해보면서 보면 정확히 설명드릴 수 있을 것 같은데 밤에 더 보완해볼게요.

1개의 좋아요

감사합니다!! 기다리고 있겠습니다 ㅎㅎ

1개의 좋아요

@Vincent 샘플소스는 정상 동작 하는 것 같네요.

그런데 dxgt:GridCardThemeKey 클래스 접근이 안됩니다.
런타임에서는 동작하지만 어떤 어셈블리를 참조해야 GridCardThemeKey를 볼 수 있는지 모르겠네요.

그리고 GridCardThemeKey은 MarkupExtension를 상속받은 클래스인 것 같습니다. 그래서 ResourceKey, THemeName 값을 통해 내부적으로 Template을 적용하는 것 같습니다.

public class GridCardThemeKey : MarkupExtension
{
     public string ResourceKey { get; set; }
     public string ThemeName { get; set; }
    
     ...
}

그리고 적용이 안되는 것은 아마 리소스 위치에 문제가 있지 않나 싶어요.

1개의 좋아요

아하…그렇다면 DevExpress측에 문의를 해봐야겠군요…도대체 어떻게 적용시키는건지 모르겠어서… 테스트 감사합니다 제임스님!

1개의 좋아요

@Vincent 앗 저도 그래서 GridCardThemeKey 관련해서 티켓 등록했는데
https://supportcenter.devexpress.com/ticket/details/t1007987/in-which-assembly-is-gridcardthemekey-included

링크가 안보이네요.

아마 데브익스프레스가 기본적으로 프라이빗 질문 티켓이고 솔루션 답변이 달리면 하단에 public으로 돌리는 기능이 따로 있으셔서 현재 프라이빗 상태신거 같습니다 ㅎㅎ

1개의 좋아요

@Vincent 다시 질문올렸는데 답변 달리면 공유해볼게요!!

1개의 좋아요

이런 링크를 찾았습니다 ㅎㅎ 저랑 비슷한 경우인거 같습니다

1개의 좋아요