WPF: Использование кнопки в качестве шаблона данных, как добавить событие клика
У меня есть ItemsControl с коллекцией объектов. Я не хочу нажимать на объект, а затем получить панели с дополнительной информацией.
Поэтому я решил стилизовать DataTemplate для элементов в ItemsControl как кнопку, и это, кажется, работает нормально. Однако я не знаю, как установить событие нажатия этой кнопки в стиле. Он говорит, что я должен использовать EventSetter, но я не могу заставить это работать.
Вот код:
<Style TargetType="Expander" >
<Style.Resources>
<Style TargetType="ItemsControl" >
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="ItemsControl">
<Border BorderThickness="0,1,0,1" BorderBrush="{StaticResource DarkColorBrush}" >
<ScrollViewer Margin="0" VerticalScrollBarVisibility="Auto"
Focusable="false">
<StackPanel Margin="2" IsItemsHost="True" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate" >
<Setter.Value>
<DataTemplate DataType="{x:Type data:CompanyViewModel}" >
<Button>
<Button.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Name="Bd" BorderBrush="{StaticResource DarkColorBrush}"
BorderThickness="1"
Margin="5"
CornerRadius="8">
<Border.Background>
<!-- Removed for brevity -->
</Border.Background>
<StackPanel Orientation="Vertical">
<TextBlock Margin="5" Text="{Binding Path=Name}" Style="{StaticResource MenuText}" FontSize="16" HorizontalAlignment="Center" />
<TextBlock Margin="5,0,5,5" Text="{Binding Path=Code, StringFormat=Kt. {0}}" Style="{StaticResource MenuText}" HorizontalAlignment="Center" />
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Bd" Property="Background">
<Setter.Value>
<!-- Removed for brevity -->
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="Button.IsPressed" Value="true">
<Setter TargetName="Bd" Property="Background">
<Setter.Value>
<!-- Removed for brevity -->
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Resources>
</Button>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="Expander">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<ToggleButton Grid.Column="1"
IsChecked="{Binding Path=IsExpanded,Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}" />
<ContentPresenter Name="Content" Grid.Column="0" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="false">
<Setter TargetName="Content" Property="Visibility" Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Решил добавить то, что хотел сделать нажатием кнопки:
<Button Click="CompanyClick" />
CompanyClick определяется в коде позади.
2 ответа
Там также это:
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<!-- Animations manipulating the button here -->
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<!-- The rest of your triggers here -->
</ControlTemplate.Triggers>
Этот вид механизма в вашем шаблоне даст вам контроль над свойствами кнопки и, возможно, свойствами в других частях визуального дерева, в зависимости от того, где вы поместили определение.
Вы могли бы также рассмотреть архитектуру вещей немного по-другому. Я не обязательно совмещал бы все определения в стиле совершенно одинаково.
+ Изменить
<Button>
Для того, чтобы...
<Button Command="{Binding OnClick}" />
В классе, который вы используете в качестве элемента в этом ItemsControl, реализуйте свойство только для чтения, которое возвращает ICommand для кнопки, которую нужно использовать.
РЕДАКТИРОВАТЬ:
В этом примере я использовал реализацию ICommand под названием RelayCommand
, который доступен по адресу http://msdn.microsoft.com/en-us/magazine/dd419663.aspx. Смотрите рисунок 3 этой статьи для полного RelayCommand
класс в C#. Я преобразовал его в Visual Basic для моего использования, этот код ниже. Он не более чем автоматизирует регистрацию команд в системе WPF и предоставляет вам удобный конструктор:
''' <summary>
''' Implements the ICommand interface
''' </summary>
''' <remarks>
''' Thanks to Josh Smith for this code: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
''' </remarks>
Public Class RelayCommand
Implements ICommand
#Region "Fields"
Private ReadOnly _execute As Action(Of Object)
Private ReadOnly _canExecute As Predicate(Of Object)
#End Region ' Fields
#Region "Constructors"
Public Sub New(ByVal execute As Action(Of Object))
Me.New(execute, Nothing)
End Sub
Public Sub New(ByVal execute As Action(Of Object), ByVal canExecute As Predicate(Of Object))
If execute Is Nothing Then
Throw New ArgumentNullException("execute")
End If
_execute = execute
_canExecute = canExecute
End Sub
#End Region ' Constructors
#Region "ICommand Members"
<DebuggerStepThrough()>
Public Function CanExecute(ByVal parameter As Object) As Boolean Implements ICommand.CanExecute
Return If(_canExecute Is Nothing, True, _canExecute(parameter))
End Function
Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
AddHandler(ByVal value As EventHandler)
AddHandler CommandManager.RequerySuggested, value
End AddHandler
RemoveHandler(ByVal value As EventHandler)
RemoveHandler CommandManager.RequerySuggested, value
End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
CommandManager.InvalidateRequerySuggested()
End RaiseEvent
End Event
Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
_execute(parameter)
End Sub
#End Region ' ICommand Members
End Class
Используя этот класс, вы можете реализовать ICommand
на вашей ViewModel, выставляя ICommand
как свойство только для чтения в этом классе вместе с полем для хранения RelayCommand, который, не забывая, реализует ICommand. Вот усеченный образец:
Public Class CompanyViewModel
Implements INotifyPropertyChanged
Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
Private _OnClick As RelayCommand
Public ReadOnly Property OnClick As ICommand
Get
If _OnClick Is Nothing Then
_OnClick = New RelayCommand(Sub()
Me.OnClickExecute()
End Sub,
Function()
Return Me.OnClickCanExecute()
End Function)
End If
Return _OnClick
End Get
End Property
Private Function OnClickCanExecute() As Boolean
' put a test here to tell the system whether conditions are right to execute your command.
' OR, just return True and it will always execute the command.
End Function
Private Sub OnClickExecute()
' put the processing for your command here; THIS IS YOUR EVENT HANDLER
End Sub
' .... implement the rest of your ViewModel
End Class
Имя "OnClick" не обязательно; Команды могут принимать любое имя, потому что система не основана на соглашениях, как VB6 со своими обработчиками событий.
Существует несколько способов сделать это. Я заинтригован реализацией ICommand "Caliburn.Micro", которая основана на соглашениях и может сделать вещи более читабельными, в зависимости от вашего стиля. Но Caliburn - это открытая работа энтузиаста, хотя и очень компетентного и квалифицированного энтузиаста. Google или Bing "Caliburn.Micro" для получения дополнительной информации об этом.