Взгляните на этот очень простой пример программы WPF:
<Window x:Class = "WpfApplication1.Window1"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
Title = "Window1" Height = "300" Width = "300">
<GroupBox>
<GroupBox.Header>
<CheckBox Content = "Click Here"/>
</GroupBox.Header>
</GroupBox>
</Window>
Итак, у меня есть GroupBox, заголовок которого - CheckBox. Мы все делали что-то подобное - обычно вы привязываете содержимое GroupBox таким образом, чтобы оно отключалось, когда CheckBox не отмечен.
Однако, когда я запускаю это приложение и нажимаю на CheckBox, я обнаружил, что иногда мои щелчки мыши проглатываются, и статус CheckBox не меняется. Если я прав, это когда я нажимаю на точную строку пикселей, на которой находится верхняя граница GroupBox.
Может кто-нибудь это продублировать? Почему это произошло и есть ли способ обойти это?
Обновлено: установка для GroupBox BorderThickness значения 0 решает проблему, но, очевидно, удаляет границу, поэтому она больше не похожа на GroupBox.
Спасибо вам большое за размещение этого! Теперь я знаю, что это ошибка.
Исправлено в .NET 4.5. Вы также можете установить .NET 4.5, чтобы исправить свое приложение 4.0.





Если вы измените BorderBrush GroupBox, он заработает!
<GroupBox BorderBrush = "{x:Null}">
Я знаю, что это противоречит цели, но это доказывает, в чем проблема!
В любом случае это может помочь. Мы могли бы каким-то образом подделать «настоящую» границу позади GroupBox. Я подожду и посмотрю, получим ли мы еще какие-либо ответы, но это может быть так близко, как я могу получить принятый ответ.
Я также спрашивал об этом в группе учеников WPF: groups.google.com/group/wpf-disciples/browse_thread/thread/…
Руди, мы просто безрезультатно пробовали. Если вы попадете в пиксель, где граница бы будет, состояние флажка не изменится. Даже установка BorderBrush на Transparent не помогает.
Ах! Установка BorderThickness на 0 исправляет это. Конечно, мы теряем границу GroupBox, что означает, что он больше не похож на GroupBox.
Вы должны установить для BorderBrush значение null, НЕ прозрачное!
Прозрачный по-прежнему является цветным и поддерживает проверку нажатия!
Ни {x: Null}, ни Transparent не работали, но BorderThickness = "0" - нет. Прозрачность иногда может влиять на проверку попадания, например, в окне, поэтому я подумал, что стоит попробовать, если null не удалось.
Похоже, это небольшая ошибка в шаблоне элемента управления для GroupBox. Я обнаружил, отредактировав шаблон по умолчанию для GroupBox и переместив границу с именем «Заголовок» на последний элемент в элементе сетки шаблонов элементов управления, проблема решается сама собой.
Причина в том, что один из других элементов Border с TemplateBinding BorderBrush находился ниже в визуальном дереве и фиксировал щелчок мыши, поэтому установка BorderBrush на null позволила CheckBox правильно получать щелчок мыши.
Ниже приведен результирующий стиль для GroupBox. Он почти идентичен шаблону по умолчанию для элемента управления, за исключением элемента Border с именем «Header», который теперь является последним дочерним элементом Grid, а не вторым.
<BorderGapMaskConverter x:Key = "BorderGapMaskConverter"/>
<Style x:Key = "GroupBoxStyle1" TargetType = "{x:Type GroupBox}">
<Setter Property = "BorderBrush" Value = "#D5DFE5"/>
<Setter Property = "BorderThickness" Value = "1"/>
<Setter Property = "Template">
<Setter.Value>
<ControlTemplate TargetType = "{x:Type GroupBox}">
<Grid SnapsToDevicePixels = "true">
<Grid.RowDefinitions>
<RowDefinition Height = "Auto"/>
<RowDefinition Height = "Auto"/>
<RowDefinition Height = "*"/>
<RowDefinition Height = "6"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width = "6"/>
<ColumnDefinition Width = "Auto"/>
<ColumnDefinition Width = "*"/>
<ColumnDefinition Width = "6"/>
</Grid.ColumnDefinitions>
<Border Grid.Column = "0" Grid.ColumnSpan = "4" Grid.Row = "1" Grid.RowSpan = "3" Background = "{TemplateBinding Background}" BorderBrush = "Transparent" BorderThickness = "{TemplateBinding BorderThickness}" CornerRadius = "4"/>
<ContentPresenter Margin = "{TemplateBinding Padding}" SnapsToDevicePixels = "{TemplateBinding SnapsToDevicePixels}" Grid.Column = "1" Grid.ColumnSpan = "2" Grid.Row = "2"/>
<Border Grid.ColumnSpan = "4" Grid.Row = "1" Grid.RowSpan = "3" BorderBrush = "White" BorderThickness = "{TemplateBinding BorderThickness}" CornerRadius = "4">
<Border.OpacityMask>
<MultiBinding Converter = "{StaticResource BorderGapMaskConverter}" ConverterParameter = "7">
<Binding Path = "ActualWidth" ElementName = "Header"/>
<Binding Path = "ActualWidth" RelativeSource = "{RelativeSource Self}"/>
<Binding Path = "ActualHeight" RelativeSource = "{RelativeSource Self}"/>
</MultiBinding>
</Border.OpacityMask>
<Border BorderBrush = "{TemplateBinding BorderBrush}" BorderThickness = "{TemplateBinding BorderThickness}" CornerRadius = "3">
<Border BorderBrush = "White" BorderThickness = "{TemplateBinding BorderThickness}" CornerRadius = "2"/>
</Border>
</Border>
<Border x:Name = "Header" Grid.Column = "1" Grid.Row = "0" Grid.RowSpan = "2" Padding = "3,1,3,0">
<ContentPresenter SnapsToDevicePixels = "{TemplateBinding SnapsToDevicePixels}" ContentSource = "Header" RecognizesAccessKey = "True"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Измененный шаблон ControlTemplate очень сложен, Ян? Хотите опубликовать это?
Спасибо, Ян! Мне пришлось добавить ресурс BorderGapConverter вне этого стиля, чтобы он работал, но как только я это сделал, он отлично справился со своей задачей.
Будьте осторожны с порядком табуляции с этим. Теперь содержимое будет перед заголовком. См. Мой ответ для получения немного другого ответа (он по-прежнему включает редактирование шаблона элемента управления).
Альтернативное решение, которое я сделал, - это реализация OnApplyTemplate в производном GroupBox:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (Children.Count == 0) return;
var grid = GetVisualChild(0) as Grid;
if (grid != null && grid.Children.Count > 3)
{
var bd = grid.Children[3] as Border;
if (bd != null)
{
bd.IsHitTestVisible = false;
}
}
}
Хорошая вещь в моем решении заключается в том, что вы не меняете внешний вид группового поля, чтобы тема по умолчанию не пострадала. Также он очень компактен.
Я использую WPF 4.0, и кажется, что GroupBox.Children не существует. Какой должен быть код для WPF 4? Должно быть, если (VisualChildrenCount == 0)?
+1, отличное решение, проще не бывает! @Mas: Вы можете заменить его на VisualChildrenCount == 0, но я думаю, что вся проверка не нужна (всегда есть один визуальный дочерний элемент). Фактически, я думаю, что все проверки не нужны (плюс вызов base), поэтому все тело метода может быть просто одной строкой: ((Grid)GetVisualChild(0)).Children[3].IsHitTestVisible = false;. Это то, что я использую, и пока это работает.
ПРЕДУПРЕЖДЕНИЕ. Кажется, что корневая ошибка была исправлена в .NET 4.5, и при установке на машину этот обходной путь приводит к тому, что все элементы управления внутри GroupBox имеют IsHitTestVisible = false, что делает их неактивными!
В порядке. Я проверю это, как только у меня будет 4.5 на моей машине, спасибо, что предупредили об этом!
В ответе Яна Оукса порядок табуляции увеличивается таким образом, что заголовок идет после содержимого. Можно изменить шаблон элемента управления так, чтобы граница не могла получать фокус.
Для этого измените шаблон так, чтобы 2-я и 3-я границы (обе в строке сетки 1) имели IsHitTestVisible=false.
Полный шаблон ниже
<BorderGapMaskConverter x:Key = "GroupBoxBorderGapMaskConverter" />
<Style x:Key = "{x:Type GroupBox}" TargetType = "{x:Type GroupBox}">
<Setter Property = "Control.BorderBrush" Value = "#FFD5DFE5" />
<Setter Property = "Control.BorderThickness" Value = "1" />
<Setter Property = "Control.Template">
<Setter.Value>
<ControlTemplate TargetType = "{x:Type GroupBox}">
<Grid SnapsToDevicePixels = "True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width = "6" />
<ColumnDefinition Width = "Auto" />
<ColumnDefinition Width = "*" />
<ColumnDefinition Width = "6" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height = "Auto" />
<RowDefinition Height = "Auto" />
<RowDefinition Height = "*" />
<RowDefinition Height = "6" />
</Grid.RowDefinitions>
<Border Name = "Header" Padding = "3,1,3,0" Grid.Row = "0" Grid.RowSpan = "2" Grid.Column = "1">
<ContentPresenter ContentSource = "Header" RecognizesAccessKey = "True" SnapsToDevicePixels = "{TemplateBinding UIElement.SnapsToDevicePixels}" />
</Border>
<Border CornerRadius = "4" Grid.Row = "1" Grid.RowSpan = "3" Grid.Column = "0" Grid.ColumnSpan = "4" BorderThickness = "{TemplateBinding Control.BorderThickness}" BorderBrush = "#00FFFFFF" Background = "{TemplateBinding Control.Background}" IsHitTestVisible = "False" />
<ContentPresenter Grid.Row = "2" Grid.Column = "1" Grid.ColumnSpan = "2" Margin = "{TemplateBinding Control.Padding}" SnapsToDevicePixels = "{TemplateBinding UIElement.SnapsToDevicePixels}"/>
<Border CornerRadius = "4" Grid.Row = "1" Grid.RowSpan = "3" Grid.ColumnSpan = "4" BorderThickness = "{TemplateBinding Control.BorderThickness}" BorderBrush = "#FFFFFFFF" IsHitTestVisible = "False">
<UIElement.OpacityMask>
<MultiBinding Converter = "{StaticResource GroupBoxBorderGapMaskConverter}" ConverterParameter = "7">
<Binding ElementName = "Header" Path = "ActualWidth" />
<Binding Path = "ActualWidth" RelativeSource = "{RelativeSource Self}" />
<Binding Path = "ActualHeight" RelativeSource = "{RelativeSource Self}" />
</MultiBinding>
</UIElement.OpacityMask>
<Border BorderThickness = "{TemplateBinding Control.BorderThickness}" BorderBrush = "{TemplateBinding Control.BorderBrush}" CornerRadius = "3">
<Border BorderThickness = "{TemplateBinding Control.BorderThickness}" BorderBrush = "#FFFFFFFF" CornerRadius = "2" />
</Border>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Давайте проголосуем за исправление этой ошибки: connect.microsoft.com/VisualStudio/feedback/details/539427/…