wpf - template - Problemas con la alineación con la selección en un cuadro de lista
wpf dropdown (1)
Estoy buscando crear una lista de rectángulos que especifiquen su ubicación mediante una coordenada x, y, sin embargo, veo un problema con la alineación de los gráficos. Estoy usando un cuadro de lista con un panel de diseño personalizado.
Aquí está el XAML para la ventana principal:
<Window x:Class="WpfFunkyPanel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfFunkyPanel"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<system:Double x:Key="barWidth">30</system:Double>
<system:Double x:Key="trackHeight">24</system:Double>
<Style x:Key="PatternGridStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="local:GridPanel.Bar" Value="{Binding Bar}"/>
<Setter Property="local:GridPanel.Track" Value="{Binding Track}"/>
</Style>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Items}" ItemContainerStyle="{StaticResource PatternGridStyle}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<local:GridPanel VerticalAlignment="Top" BarWidth="{StaticResource barWidth}" TrackHeight="{StaticResource trackHeight}">
<local:GridPanel.Background>
<DrawingBrush TileMode="Tile" ViewboxUnits="Absolute" ViewportUnits="Absolute">
<DrawingBrush.Viewbox>
<Rect X="0" Y="0" Width="{StaticResource barWidth}" Height="{StaticResource trackHeight}"/>
</DrawingBrush.Viewbox>
<DrawingBrush.Viewport>
<Rect X="1" Y="0" Width="{StaticResource barWidth}" Height="{StaticResource trackHeight}"/>
</DrawingBrush.Viewport>
<DrawingBrush.Drawing>
<GeometryDrawing Brush="LightGray">
<GeometryDrawing.Pen>
<Pen Brush="Black" Thickness="1"/>
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<PathGeometry>
<PathFigure IsFilled="True">
<LineSegment>
<LineSegment.Point>
<Point X="{StaticResource barWidth}" Y="0"/>
</LineSegment.Point>
</LineSegment>
<LineSegment>
<LineSegment.Point>
<Point X="{StaticResource barWidth}" Y="{StaticResource trackHeight}"/>
</LineSegment.Point>
</LineSegment>
<LineSegment>
<LineSegment.Point>
<Point X="0" Y="{StaticResource trackHeight}"/>
</LineSegment.Point>
</LineSegment>
<LineSegment/>
</PathFigure>
</PathGeometry>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</local:GridPanel.Background>
</local:GridPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,1,0,1">
<Rectangle Fill="Gray" Width="28" Height="22"></Rectangle>
<TextBlock Text="{Binding Text}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
Aquí está el panel de la cuadrícula:
using System;
namespace WpfFunkyPanel
{
using System.Windows;
using System.Windows.Controls;
public class GridPanel : Panel
{
public static readonly DependencyProperty BarProperty = DependencyProperty.RegisterAttached(
"Bar",
typeof(int),
typeof(GridPanel));
public static readonly DependencyProperty TrackProperty = DependencyProperty.RegisterAttached(
"Track",
typeof(int),
typeof(GridPanel));
public static readonly DependencyProperty BarWidthProperty = DependencyProperty.Register(
"BarWidth",
typeof(double),
typeof(GridPanel),
new FrameworkPropertyMetadata(
28.0,
FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
public static readonly DependencyProperty TrackHeightProperty = DependencyProperty.Register(
"TrackHeight",
typeof(double),
typeof(GridPanel),
new FrameworkPropertyMetadata(
24.0,
FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
public static int GetBar(DependencyObject obj)
{
return (int)obj.GetValue(BarProperty);
}
public static void SetBar(DependencyObject obj, int value)
{
obj.SetValue(BarProperty, value);
}
public static int GetTrack(DependencyObject obj)
{
return (int)obj.GetValue(TrackProperty);
}
public static void SetTrack(DependencyObject obj, int value)
{
obj.SetValue(TrackProperty, value);
}
public GridPanel()
{
}
protected override Size MeasureOverride(Size availableSize)
{
Size size = new Size();
foreach (UIElement child in InternalChildren)
{
int bar = GetBar(child);
int track = GetTrack(child);
double width = (bar + 1) * BarWidth;
double height = (track + 1) * TrackHeight;
size.Width = Math.Max(size.Width, width);
size.Height = Math.Max(size.Height, height);
}
return size;
}
protected override Size ArrangeOverride(Size finalSize)
{
foreach (UIElement child in InternalChildren)
{
int bar = GetBar(child);
int track = GetTrack(child);
Rect rect = new Rect(bar * BarWidth, track * TrackHeight, BarWidth, TrackHeight);
child.Arrange(rect);
}
return finalSize;
}
public double BarWidth
{
get
{
return (double)this.GetValue(BarWidthProperty);
}
set
{
SetValue(BarWidthProperty, value);
}
}
public double TrackHeight
{
get
{
return (double)this.GetValue(TrackHeightProperty);
}
set
{
SetValue(TrackHeightProperty, value);
}
}
}
}
Aquí está el elemento del patrón:
namespace WpfFunkyPanel
{
public class PatternItem
{
private readonly int bar;
private readonly int track;
private readonly string text;
public PatternItem(int bar, int track, string text)
{
this.bar = bar;
this.track = track;
this.text = text;
}
public int Bar
{
get
{
return bar;
}
}
public int Track
{
get
{
return track;
}
}
public string Text
{
get
{
return text;
}
}
}
}
Aquí está el modelo de vista de colección:
namespace WpfFunkyPanel
{
using System.Collections.ObjectModel;
public class PatternContainer
{
private int bars;
private int tracks;
private ObservableCollection<PatternItem> items;
public PatternContainer()
{
bars = 32;
tracks = 24;
items = new ObservableCollection<PatternItem>();
}
public void Add(PatternItem item)
{
items.Add(item);
}
public int Bars
{
get
{
return bars;
}
}
public int Tracks
{
get
{
return tracks;
}
}
public ObservableCollection<PatternItem> Items
{
get
{
return items;
}
}
}
}
Finalmente, el código de la ventana principal detrás:
usando System.Windows;
namespace WpfFunkyPanel
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var container = new PatternContainer();
for (int bar = 0; bar < 16; bar++)
{
container.Add(new PatternItem(bar, 0, "A" + (bar + 1).ToString()));
}
container.Add(new PatternItem(3, 2, "C4"));
container.Add(new PatternItem(5, 5, "G6"));
DataContext = container;
}
}
}
Esto es lo que parece, con un rectángulo seleccionado:
El problema es que el cuadro de selección en azul está desplazado a la izquierda. Además, para que las cosas se alineen, tuve que configurar la ventana gráfica en 1,0, w, h, no 0,0, w, h, como hubiera esperado.
Encontré una solución: crear mis propios controles GridControl (derivado de Selector) y GridItemControl (ContentControl) y ahora todo tiene sentido.
Aquí está mi GridControlItem:
public class GridControlItem : ContentControl
{
public static readonly DependencyProperty BarProperty = DependencyProperty.Register(
"Bar",
typeof(int),
typeof(GridControlItem),
new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public static readonly DependencyProperty TrackProperty = DependencyProperty.Register(
"Track",
typeof(int),
typeof(GridControlItem),
new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public static readonly DependencyProperty PickedProperty = DependencyProperty.Register(
"Picked",
typeof(bool),
typeof(GridControlItem),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public int Bar
{
get
{
return (int)this.GetValue(BarProperty);
}
set
{
SetValue(BarProperty, value);
}
}
public int Track
{
get
{
return (int)this.GetValue(TrackProperty);
}
set
{
SetValue(TrackProperty, value);
}
}
public bool Picked
{
get
{
return (bool)this.GetValue(PickedProperty);
}
set
{
SetValue(PickedProperty, value);
}
}
static GridControlItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(GridControlItem), new FrameworkPropertyMetadata(typeof(GridControlItem)));
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);
if (e.ChangedButton == MouseButton.Left)
{
this.Picked = true;
e.Handled = true;
}
}
}
Deliberadamente utilicé "Seleccionado" en lugar de "Seleccionado" para asegurarme de que esto funciona.
Ahora el GridControl:
public class GridControl : Selector
{
static GridControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(GridControl), new FrameworkPropertyMetadata(typeof(GridControl)));
}
protected override DependencyObject GetContainerForItemOverride()
{
return new GridControlItem();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return (item is GridControlItem);
}
}
y Generic.xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PatternControlLibrary"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:Double x:Key="barWidth">30</system:Double>
<system:Double x:Key="trackHeight">24</system:Double>
<Style TargetType="{x:Type local:GridControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:GridControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">
<ItemsPresenter />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<local:GridPanel VerticalAlignment="Top" BarWidth="{StaticResource barWidth}" TrackHeight="{StaticResource trackHeight}">
<local:GridPanel.Background>
<DrawingBrush TileMode="Tile" ViewboxUnits="Absolute" ViewportUnits="Absolute">
<DrawingBrush.Viewbox>
<Rect X="0" Y="0" Width="{StaticResource barWidth}" Height="{StaticResource trackHeight}"/>
</DrawingBrush.Viewbox>
<DrawingBrush.Viewport>
<Rect X="0" Y="0" Width="{StaticResource barWidth}" Height="{StaticResource trackHeight}"/>
</DrawingBrush.Viewport>
<DrawingBrush.Drawing>
<GeometryDrawing Brush="LightGray">
<GeometryDrawing.Pen>
<Pen Brush="Black" Thickness="1"/>
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<PathGeometry>
<PathFigure IsFilled="True">
<LineSegment>
<LineSegment.Point>
<Point X="{StaticResource barWidth}" Y="0"/>
</LineSegment.Point>
</LineSegment>
<LineSegment>
<LineSegment.Point>
<Point X="{StaticResource barWidth}" Y="{StaticResource trackHeight}"/>
</LineSegment.Point>
</LineSegment>
<LineSegment>
<LineSegment.Point>
<Point X="0" Y="{StaticResource trackHeight}"/>
</LineSegment.Point>
</LineSegment>
<LineSegment/>
</PathFigure>
</PathGeometry>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</local:GridPanel.Background>
</local:GridPanel>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:GridControlItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:GridControlItem}">
<Border x:Name="Border">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Picked" Value="True">
<Setter TargetName="Border" Property="Background" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="local:GridPanel.Bar" Value="{Binding Bar}"/>
<Setter Property="local:GridPanel.Track" Value="{Binding Track}"/>
</Style>
</ResourceDictionary>
Aquí está el MainWindow.xaml:
<Window x:Class="WpfFunkyPanel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:cc="clr-namespace:PatternControlLibrary;assembly=PatternControlLibrary"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="dataItemTemplate">
<Grid>
<Rectangle Fill="LightPink" Width="28" Height="22"/>
<TextBlock Text="{Binding Text}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<cc:GridControl
ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource dataItemTemplate}"/>
</Grid>
</Window>