I've created a UserControl, PortStatusButton, that contains nothing but a RadButton for which I've modified its standard style to incorporate my own VisualStateGroup with its VisualStates. Its purpose is to be a multistate button with logic based on the values of several of its properties. I have written a custom VisualStateManager to process that logic. I have placed the PortStatusButton into the ItemTemplate of a RadListBox and bound its properties to data in my Model.
The basic method I've used to incorporate my visual states into the button is to create a separate VisualStateGroup and then have my VisualStateManager watch for a call to GoToStateCore for a CommonStates.Normal VisualState. I then check to see what state my button should be in and then call GoToState to go to my desired state. This allows me to have the PortStatusButton show my states, but still let the RadButton control the overall functioning of the button.
It's all working quite well, with one exception. When the PortStatusButton first appears, it fails to display my correct VisualState, but displays the CommonStates.Normal state instead. I think I know why and I'd like to ask you to verify that I'm correct and suggest what I might do about it.
The mode of failure is that the first time a property gets set in PortStatusButton, the UpdateButtonAppearance method that is called by its OnPropertyChanged handler fails to obtain a reference to the the current state of my VisualStateGroup because it can't find the child Grid of the RadButton that contains the VisualStateManager. Thus, it never calls GoToState because it doesn't know where to go, and the RadButton first displays in its CommonStates.Normal state.
The data that is feeding the bound PortStatusButton properties is coming from my ViewModel, which is defined as the DataContext in the main Grid that contains the RadListBox, and which loads the source collection in its constructor. My suspicion is that as each PortStatusButton is instantiated by the RadListBox, the Grid in the RadButton's control template has not yet been instantiated, so it's not yet there to be found. By the time the user starts clicking on the buttons, the Grid has been instantiated and the PortStatusButtons will function as they should.
Is that correct?
What would you suggest I do to allow the buttons to display my correct state when they first appear?
I have included excerpts from my code below:
First, excerpt from my PortStatusButton xaml:
<
UserControl.Resources
>
<
Style
x:Key
=
"PortStatusButtonStyle"
TargetType
=
"{x:Type telerik:RadButton}"
>
<
Setter
Property
=
"BorderThickness"
Value
=
"1"
/>
<
Setter
Property
=
"Foreground"
Value
=
"Black"
/>
<
Setter
Property
=
"Background"
>
<
Setter.Value
>
<
LinearGradientBrush
EndPoint
=
"0.5,1"
StartPoint
=
"0.5,0"
>
<
GradientStop
Color
=
"White"
Offset
=
"0"
/>
<
GradientStop
Color
=
"Gainsboro"
Offset
=
"0.5"
/>
<
GradientStop
Color
=
"#FFADADAD"
Offset
=
"0.5"
/>
<
GradientStop
Color
=
"#FFD4D4D4"
Offset
=
"1"
/>
</
LinearGradientBrush
>
</
Setter.Value
>
</
Setter
>
<
Setter
Property
=
"BorderBrush"
Value
=
"#FF848484"
/>
<
Setter
Property
=
"CornerRadius"
Value
=
"1"
/>
<
Setter
Property
=
"Padding"
Value
=
"3"
/>
<
Setter
Property
=
"FocusVisualStyle"
Value
=
"{x:Null}"
/>
<
Setter
Property
=
"HorizontalContentAlignment"
Value
=
"Center"
/>
<
Setter
Property
=
"VerticalContentAlignment"
Value
=
"Center"
/>
<
Setter
Property
=
"Template"
>
<
Setter.Value
>
<
ControlTemplate
TargetType
=
"{x:Type telerik:RadButton}"
>
<
Grid
Name
=
"theStateGroupsRoot"
SnapsToDevicePixels
=
"True"
>
<
VisualStateManager.CustomVisualStateManager
>
<
local:PortStatusButtonVisualStateManager
/>
</
VisualStateManager.CustomVisualStateManager
>
<
VisualStateManager.VisualStateGroups
>
<!-- RadButton VisualStateGroups-->
<
VisualStateGroup
x:Name
=
"ConditionStates"
>
<
VisualState
x:Name
=
"Inactive"
>
<
Storyboard
>
<
ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty
=
"Visibility"
Storyboard.TargetName
=
"InactiveBorder"
>
<
DiscreteObjectKeyFrame
KeyTime
=
"0"
>
<
DiscreteObjectKeyFrame.Value
>
<
Visibility
>Visible</
Visibility
>
</
DiscreteObjectKeyFrame.Value
>
</
DiscreteObjectKeyFrame
>
</
ObjectAnimationUsingKeyFrames
>
<
DoubleAnimation
Duration
=
"0"
To
=
"0.5"
Storyboard.TargetProperty
=
"(UIElement.Opacity)"
Storyboard.TargetName
=
"InactiveBorder"
/>
<
DoubleAnimation
Duration
=
"0"
To
=
"1"
Storyboard.TargetProperty
=
"Opacity"
Storyboard.TargetName
=
"InactiveContent"
/>
<
DoubleAnimation
Duration
=
"0"
To
=
"0"
Storyboard.TargetProperty
=
"Opacity"
Storyboard.TargetName
=
"ActiveContent"
/>
<
DoubleAnimation
Duration
=
"0"
To
=
"0"
Storyboard.TargetProperty
=
"Opacity"
Storyboard.TargetName
=
"ConnectedContent"
/>
</
Storyboard
>
</
VisualState
>
<
VisualState
x:Name
=
"Active"
>
<
Storyboard
>
<
ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty
=
"Visibility"
Storyboard.TargetName
=
"ActiveBorder"
>
<
DiscreteObjectKeyFrame
KeyTime
=
"0"
>
<
DiscreteObjectKeyFrame.Value
>
<
Visibility
>Visible</
Visibility
>
</
DiscreteObjectKeyFrame.Value
>
</
DiscreteObjectKeyFrame
>
</
ObjectAnimationUsingKeyFrames
>
<
DoubleAnimation
Duration
=
"0"
To
=
"0.5"
Storyboard.TargetProperty
=
"(UIElement.Opacity)"
Storyboard.TargetName
=
"ActiveBorder"
/>
<
DoubleAnimation
Duration
=
"0"
To
=
"0"
Storyboard.TargetProperty
=
"Opacity"
Storyboard.TargetName
=
"InactiveContent"
/>
<
DoubleAnimation
Duration
=
"0"
To
=
"1"
Storyboard.TargetProperty
=
"Opacity"
Storyboard.TargetName
=
"ActiveContent"
/>
<
DoubleAnimation
Duration
=
"0"
To
=
"0"
Storyboard.TargetProperty
=
"Opacity"
Storyboard.TargetName
=
"ConnectedContent"
/>
</
Storyboard
>
</
VisualState
>
<
VisualState
x:Name
=
"Connected"
>
<
Storyboard
>
<
ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty
=
"Visibility"
Storyboard.TargetName
=
"ConnectedBorder"
>
<
DiscreteObjectKeyFrame
KeyTime
=
"0"
>
<
DiscreteObjectKeyFrame.Value
>
<
Visibility
>Visible</
Visibility
>
</
DiscreteObjectKeyFrame.Value
>
</
DiscreteObjectKeyFrame
>
</
ObjectAnimationUsingKeyFrames
>
<
DoubleAnimation
Duration
=
"0"
To
=
"0.5"
Storyboard.TargetProperty
=
"(UIElement.Opacity)"
Storyboard.TargetName
=
"ConnectedBorder"
/>
<
DoubleAnimation
Duration
=
"0"
To
=
"0"
Storyboard.TargetProperty
=
"Opacity"
Storyboard.TargetName
=
"InactiveContent"
/>
<
DoubleAnimation
Duration
=
"0"
To
=
"0"
Storyboard.TargetProperty
=
"Opacity"
Storyboard.TargetName
=
"ActiveContent"
/>
<
DoubleAnimation
Duration
=
"0"
To
=
"1"
Storyboard.TargetProperty
=
"Opacity"
Storyboard.TargetName
=
"ConnectedContent"
/>
</
Storyboard
>
</
VisualState
>
</
VisualStateGroup
>
</
VisualStateManager.VisualStateGroups
>
<!-- RadButton border declarations -->
<
Border
x:Name
=
"InactiveBorder"
BorderBrush
=
"#FF848484"
BorderThickness
=
"{TemplateBinding BorderThickness}"
CornerRadius
=
"{TemplateBinding CornerRadius}"
Opacity
=
"0"
>
<
Border.Background
>
<
LinearGradientBrush
EndPoint
=
"0.5,1"
StartPoint
=
"0.5,0"
>
<
GradientStop
Color
=
"White"
Offset
=
"0"
/>
<
GradientStop
Color
=
"#FF7E7E7E"
Offset
=
"0.5"
/>
<
GradientStop
Color
=
"#FF747474"
Offset
=
"0.5"
/>
<
GradientStop
Color
=
"#FFA0A0A0"
Offset
=
"1"
/>
</
LinearGradientBrush
>
</
Border.Background
>
<
Border
BorderBrush
=
"White"
BorderThickness
=
"{TemplateBinding BorderThickness}"
Background
=
"{x:Null}"
CornerRadius
=
"{TemplateBinding InnerCornerRadius}"
/>
</
Border
>
<
Border
x:Name
=
"ActiveBorder"
BorderBrush
=
"#FF848484"
BorderThickness
=
"{TemplateBinding BorderThickness}"
CornerRadius
=
"{TemplateBinding CornerRadius}"
Opacity
=
"0"
>
<
Border.Background
>
<
LinearGradientBrush
EndPoint
=
"0.5,1"
StartPoint
=
"0.5,0"
>
<
GradientStop
Color
=
"White"
Offset
=
"0"
/>
<
GradientStop
Color
=
"#FFFF7171"
Offset
=
"0.5"
/>
<
GradientStop
Color
=
"Red"
Offset
=
"0.5"
/>
<
GradientStop
Color
=
"#FFFF9090"
Offset
=
"1"
/>
</
LinearGradientBrush
>
</
Border.Background
>
<
Border
BorderBrush
=
"White"
BorderThickness
=
"{TemplateBinding BorderThickness}"
Background
=
"{x:Null}"
CornerRadius
=
"{TemplateBinding InnerCornerRadius}"
/>
</
Border
>
<
Border
x:Name
=
"ConnectedBorder"
BorderBrush
=
"#FF848484"
BorderThickness
=
"{TemplateBinding BorderThickness}"
CornerRadius
=
"{TemplateBinding CornerRadius}"
Opacity
=
"0"
>
<
Border.Background
>
<
LinearGradientBrush
EndPoint
=
"0.5,1"
StartPoint
=
"0.5,0"
>
<
GradientStop
Color
=
"White"
Offset
=
"0"
/>
<
GradientStop
Color
=
"#FF59FD59"
Offset
=
"0.5"
/>
<
GradientStop
Color
=
"Lime"
Offset
=
"0.5"
/>
<
GradientStop
Color
=
"#FF96FF96"
Offset
=
"1"
/>
</
LinearGradientBrush
>
</
Border.Background
>
<
Border
BorderBrush
=
"White"
BorderThickness
=
"{TemplateBinding BorderThickness}"
Background
=
"{x:Null}"
CornerRadius
=
"{TemplateBinding InnerCornerRadius}"
/>
</
Border
>
<!-- More RadButton Border declarations -->
<
ContentPresenter
x:Name
=
"Content"
ContentTemplate
=
"{TemplateBinding ContentTemplate}"
Content
=
"{TemplateBinding Content}"
ContentStringFormat
=
"{TemplateBinding ContentStringFormat}"
TextElement.Foreground
=
"{TemplateBinding Foreground}"
HorizontalAlignment
=
"{TemplateBinding HorizontalContentAlignment}"
Margin
=
"{TemplateBinding Padding}"
RecognizesAccessKey
=
"True"
VerticalAlignment
=
"{TemplateBinding VerticalContentAlignment}"
/>
<
ContentPresenter
x:Name
=
"InactiveContent"
ContentTemplate
=
"{TemplateBinding ContentTemplate}"
Content
=
"Inactive"
ContentStringFormat
=
"{TemplateBinding ContentStringFormat}"
TextElement.Foreground
=
"{TemplateBinding Foreground}"
HorizontalAlignment
=
"{TemplateBinding HorizontalContentAlignment}"
Margin
=
"{TemplateBinding Padding}"
RecognizesAccessKey
=
"True"
VerticalAlignment
=
"{TemplateBinding VerticalContentAlignment}"
Opacity
=
"0"
/>
<
ContentPresenter
x:Name
=
"ActiveContent"
ContentTemplate
=
"{TemplateBinding ContentTemplate}"
Content
=
"Active"
ContentStringFormat
=
"{TemplateBinding ContentStringFormat}"
TextElement.Foreground
=
"{TemplateBinding Foreground}"
HorizontalAlignment
=
"{TemplateBinding HorizontalContentAlignment}"
Margin
=
"{TemplateBinding Padding}"
RecognizesAccessKey
=
"True"
VerticalAlignment
=
"{TemplateBinding VerticalContentAlignment}"
Opacity
=
"0"
/>
<
ContentPresenter
x:Name
=
"ConnectedContent"
ContentTemplate
=
"{TemplateBinding ContentTemplate}"
Content
=
"Connected"
ContentStringFormat
=
"{TemplateBinding ContentStringFormat}"
TextElement.Foreground
=
"{TemplateBinding Foreground}"
HorizontalAlignment
=
"{TemplateBinding HorizontalContentAlignment}"
Margin
=
"{TemplateBinding Padding}"
RecognizesAccessKey
=
"True"
VerticalAlignment
=
"{TemplateBinding VerticalContentAlignment}"
Opacity
=
"0"
/>
<
Border
x:Name
=
"CommonStatesWrapper"
>
<
Border
x:Name
=
"FocusVisual"
BorderBrush
=
"#FFFFC92B"
BorderThickness
=
"{TemplateBinding BorderThickness}"
CornerRadius
=
"{TemplateBinding CornerRadius}"
Visibility
=
"Collapsed"
>
<
Border
BorderBrush
=
"Transparent"
BorderThickness
=
"1"
CornerRadius
=
"{TemplateBinding InnerCornerRadius}"
/>
</
Border
>
</
Border
>
</
Grid
>
</
ControlTemplate
>
</
Setter.Value
>
</
Setter
>
</
Style
>
</
UserControl.Resources
>
<
Grid
Name
=
"theGrid"
>
<
telerik:RadButton
Name
=
"theButton"
Style
=
"{DynamicResource PortStatusButtonStyle}"
Click
=
"TheButton_Click"
/>
</
Grid
>
</
UserControl
>
And an excerpt from my PortStatusButton code behind:
public
partial
class
PortStatusButton : UserControl
{
// Constants
// ConditionStates VisualStateGroup constants
public
const
string
cConditionVisualStateGroup =
"ConditionStates"
;
public
const
string
cActiveVisualState =
"Active"
;
public
const
string
cInactiveVisualState =
"Inactive"
;
public
const
string
cConnectedVisualState =
"Connected"
;
// CommonStates VisualStateGroup constants
public
const
string
cCommonGroupVisualStateGroup =
"CommonStates"
;
public
const
string
cNormalVisualState =
"Normal"
;
// Properties
private
bool
IsSwitchOn {
get
;
set
; }
public
int
PortNumber {
get
{
return
(
int
)GetValue(PortNumberProperty); }
set
{ SetValue(PortNumberProperty, value); } }
public
static
readonly
DependencyProperty PortNumberProperty = DependencyProperty.Register(
"PortNumber"
,
typeof
(
int
),
typeof
(PortStatusButton),
new
PropertyMetadata(OnPortNumberChanged));
public
int
RuleIndex {
get
{
return
(
int
)GetValue(RuleIndexProperty); }
set
{ SetValue(RuleIndexProperty, value); } }
public
static
readonly
DependencyProperty RuleIndexProperty = DependencyProperty.Register(
"RuleIndex"
,
typeof
(
int
),
typeof
(PortStatusButton),
new
PropertyMetadata(OnRuleIndexChanged));
// Constructors
public
PortStatusButton()
{
IsSwitchOn =
false
;
InitializeComponent();
}
// Methods
private
void
UpdateButtonAppearance(PortStatusButton control)
{
// Rules:
// +------------+-----------+--------++===========+
// | PortNumber | RuleIndex | Switch || Button |
// | Assigned | Assigned | On || State |
// +------------+-----------+--------++===========+
// | 0 | 0 | 0 || Inactive |
// +------------+-----------+--------++===========+
// | 0 | 0 | 1 || Inactive |
// +------------+-----------+--------++===========+
// | 0 | 1 | 1 || Inactive |
// +------------+-----------+--------++===========+
// | 0 | 1 | 0 || Inactive |
// +------------+-----------+--------++===========+
// | 1 | 1 | 0 || Inactive |
// +------------+-----------+--------++===========+
// | 1 | 1 | 1 || Connected |
// +------------+-----------+--------++===========+
// | 1 | 0 | 1 || Active |
// +------------+-----------+--------++===========+
// | 1 | 0 | 0 || Inactive |
// +------------+-----------+--------++===========+
//
// Boolean equations:
// Inactive = Not(PortNumber Assigned) + ((PortNumber Assigned) * (Not(Switch On))
// Active = (PortNumber Assigned) * NOT(RuleIndex Assigned) * (Switch On)
// Connected = (PortNumberAssigned) * (RuleIndex Assigned) * (Switch On)
bool
IsPortNumberAssigned = PortNumber > PortConstants.UnassignedPortNumber;
bool
IsRuleIndexAssigned = RuleIndex > PortConstants.UnassignedRuleIndex;
string
NewStateName = cInactiveVisualState;
if
(IsPortNumberAssigned)
{
if
(IsSwitchOn)
{
if
(IsRuleIndexAssigned)
{
NewStateName = cConnectedVisualState;
}
else
{
NewStateName = cActiveVisualState;
}
}
}
FrameworkElement Root = VisualStateManagerHelper.FindVisualStateGroupsRoot(control.theButton);
if
(Root !=
null
)
{
VisualStateGroup Group = VisualStateManagerHelper.GetVisualStateGroup(cConditionVisualStateGroup, Root);
VisualState CurrentState = Group.CurrentState;
bool
StateWasChanged =
false
;
if
(CurrentState ==
null
)
{
StateWasChanged = VisualStateManager.GoToState(control.theButton, NewStateName,
false
);
CurrentState = Group.CurrentState;
}
if
(NewStateName != CurrentState.Name)
{
StateWasChanged = VisualStateManager.GoToState(control.theButton, NewStateName,
false
);
}
}
}
private
static
void
OnPortNumberChanged (DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PortStatusButton Me = (d
is
PortStatusButton) ? (PortStatusButton)d :
null
;
if
(Me !=
null
)
{
Me.UpdateButtonAppearance(d
as
PortStatusButton);
}
}
private
static
void
OnRuleIndexChanged (DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PortStatusButton Me = (d
is
PortStatusButton) ? (PortStatusButton)d :
null
;
if
(Me !=
null
)
{
Me.UpdateButtonAppearance(d
as
PortStatusButton);
}
}
private
void
TheButton_Click(
object
sender, RoutedEventArgs e)
{
PortStatusButton Ancestor = ZWVisualTreeHelper.FindParent<PortStatusButton>(sender
as
DependencyObject);
IsSwitchOn = !IsSwitchOn;
UpdateButtonAppearance(Ancestor);
}
}
And the custom VisualStateManager:
public
class
PortStatusButtonVisualStateManager : VisualStateManagerHelper
{
// Methods
protected
override
bool
GoToStateCore(FrameworkElement control, FrameworkElement stateGroupsRoot,
string
stateName, VisualStateGroup group, VisualState state,
bool
useTransitions)
{
bool
IsGood =
false
;
string
NewStateName =
""
;
bool
ShouldTransitionToNewState =
false
;
if
((group !=
null
) && (state !=
null
))
{
PortStatusButton Me = ZWVisualTreeHelper.FindParent<PortStatusButton>(control);
// The basic premise on how this works is that the ConditionGroup visual states override the Normal state of the CommonStates group.
//
// The RadButton CommonGroup visual states control the button in its "steady state" condition. Normal is its default visual state and the button is in this state unless something else it happening.
// The other Common states handle it when disabled, pressed, or the mouse is hovering over it. These are all mutually exclusive and form the base appearance of the button.
//
// The other state groups likewise each contain mutually exclusive visual states, but they modify the appearance of the button when it's any of the Common states. So, the final appearance of the
// button is the result of the Common state it in, as modified by the states of the other groups.
//
// I wanted to take advantage of this already-existing functionality and tie into the Common group, but I felt it was important to keep my states separate because I wasn't sure how they would "play"
// with the other groups when in a non-normal state. After all, I'm not sure yet how I want the button to appear when it's disabled, for instance. Will it depend on the state of the Condition?
//
// What I arrived at is that I want to override the CommonGroup.Normal state with one of the ConditionGroup states, but leave the other CommonGroup states alone to do their own thing. So, here
// we will examine the requested new state to see if it is CommonGroup.Normal, and if it is, we'll chage the state to the current state of the ConditionGroup.
if
((group.Name == PortStatusButton.cCommonGroupVisualStateGroup) && (state.Name == PortStatusButton.cNormalVisualState))
{
VisualStateGroup NewGroup = GetVisualStateGroup(PortStatusButton.cConditionVisualStateGroup, stateGroupsRoot);
VisualState NewState =
null
;
if
(NewGroup !=
null
)
{
NewState = NewGroup.CurrentState;
if
(NewState ==
null
)
{
}
else
{
NewStateName = NewState.Name;
ShouldTransitionToNewState =
true
;
}
}
}
IsGood =
base
.GoToStateCore(control, stateGroupsRoot, stateName, group, state, useTransitions);
if
(ShouldTransitionToNewState)
{
IsGood = GoToState(control, NewStateName,
false
);
}
}
return
IsGood;
}
}
And an excerpt from the control containing the RadListBox:
<
UserControl.Resources
>
<
Style
x:Key
=
"DraggableListBoxItem"
TargetType
=
"telerik:RadListBoxItem"
BasedOn
=
"{StaticResource RadListBoxItemStyle}"
>
<
Setter
Property
=
"telerik:DragDropManager.AllowCapturedDrag"
Value
=
"True"
/>
</
Style
>
</
UserControl.Resources
>
<
Grid
IsSharedSizeScope
=
"True"
Width
=
"Auto"
>
<
Grid.DataContext
>
<
vm:InputPortViewModel
/>
</
Grid.DataContext
>
<
Grid.RowDefinitions
>
<
RowDefinition
SharedSizeGroup
=
"ConfiguratorMajorHeaderRowGroup"
/>
<
RowDefinition
SharedSizeGroup
=
"ConfiguratorHeaderRowGroup"
/>
<
RowDefinition
SharedSizeGroup
=
"ConfiguratorListRowGroup"
/>
<
RowDefinition
SharedSizeGroup
=
"ConfiguratorFooterRowGroup"
/>
</
Grid.RowDefinitions
>
<
TextBlock
Grid.Row
=
"0"
Text
=
"Input Ports"
HorizontalAlignment
=
"Center"
/>
<
Border
Grid.Row
=
"0"
BorderBrush
=
"Black"
BorderThickness
=
"0,0,1,1"
/>
<
Grid
Grid.Row
=
"1"
>
<
Grid.ColumnDefinitions
>
<
ColumnDefinition
Width
=
"*"
/>
<
ColumnDefinition
SharedSizeGroup
=
"InputPortsStatusButtonColumnGroup"
/>
<
ColumnDefinition
SharedSizeGroup
=
"InputPortNumberColumnGroup"
/>
<
ColumnDefinition
SharedSizeGroup
=
"InputPortHyphenColumnGroup"
/>
<
ColumnDefinition
SharedSizeGroup
=
"InputPortNameColumnGroup"
/>
<
ColumnDefinition
Width
=
"*"
/>
</
Grid.ColumnDefinitions
>
<
Border
Grid.Column
=
"1"
Grid.ColumnSpan
=
"6"
BorderBrush
=
"Black"
BorderThickness
=
"0,0,1,0"
/>
<
TextBlock
Grid.Column
=
"1"
Text
=
"Status"
HorizontalAlignment
=
"Center"
/>
<
TextBlock
Grid.Column
=
"2"
Text
=
"#"
HorizontalAlignment
=
"Center"
/>
<
TextBlock
Grid.Column
=
"3"
Text
=
""
/>
<
TextBlock
Grid.Column
=
"4"
Text
=
"Name"
HorizontalAlignment
=
"Center"
/>
</
Grid
>
<
telerik:RadListBox
Grid.Row
=
"2"
Name
=
"PortListBox"
ItemsSource
=
"{Binding Ports}"
ItemContainerStyle
=
"{StaticResource DraggableListBoxItem}"
>
<
telerik:RadListBox.ItemTemplate
>
<
DataTemplate
>
<
Grid
>
<
Grid.ColumnDefinitions
>
<
ColumnDefinition
SharedSizeGroup
=
"InputPortsStatusButtonColumnGroup"
/>
<
ColumnDefinition
SharedSizeGroup
=
"InputPortNumberColumnGroup"
/>
<
ColumnDefinition
SharedSizeGroup
=
"InputPortHyphenColumnGroup"
/>
<
ColumnDefinition
SharedSizeGroup
=
"InputPortNameColumnGroup"
/>
</
Grid.ColumnDefinitions
>
<
local:PortStatusButton
Grid.Column
=
"0"
x:Name
=
"thePortStatusButton"
PortNumber
=
"{Binding PortNumber}"
RuleIndex
=
"{Binding RuleIndex}"
/>
<
TextBlock
Grid.Column
=
"1"
Name
=
"thePortNumber"
Text
=
"{Binding PortNumber}"
FontWeight
=
"Bold"
HorizontalAlignment
=
"Right"
Margin
=
"5,0,0,0"
/>
<
TextBlock
Grid.Column
=
"2"
Text
=
"-"
HorizontalAlignment
=
"Center"
Margin
=
"5,0,5,0"
/>
<
TextBlock
Grid.Column
=
"3"
Name
=
"theDisplayName"
Text
=
"{Binding DisplayName}"
Margin
=
"0,0,5,0"
/>
</
Grid
>
</
DataTemplate
>
</
telerik:RadListBox.ItemTemplate
>
<
telerik:RadListBox.DragDropBehavior
>
<
telerik:ListBoxDragDropBehavior
/>
</
telerik:RadListBox.DragDropBehavior
>
<
telerik:RadListBox.DragVisualProvider
>
<
telerik:ListBoxDragVisualProvider
/>
</
telerik:RadListBox.DragVisualProvider
>
</
telerik:RadListBox
>
<
StackPanel
Grid.Row
=
"3"
Orientation
=
"Horizontal"
HorizontalAlignment
=
"Center"
>
<
TextBlock
Name
=
"theCount"
Text
=
"{Binding Ports.Count}"
/>
<
TextBlock
Text
=
"Input Ports"
Margin
=
"5,0,0,0"
/>
</
StackPanel
>
<
Border
Grid.Row
=
"3"
BorderBrush
=
"Black"
BorderThickness
=
"0,0,1,1"
/>
</
Grid
>
</
UserControl
>