Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit 834e7c1

Browse files
authored
Merge pull request #416 from github/fixes/414-linkdropdown
Added LinkDropDown control.
2 parents 97d81b5 + e2a12b4 commit 834e7c1

File tree

7 files changed

+211
-146
lines changed

7 files changed

+211
-146
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
using System.Windows;
2+
using System.Windows.Controls;
3+
4+
namespace GitHub.UI
5+
{
6+
/// <summary>
7+
/// A ComboBox that displays as a link with a dropdown.
8+
/// </summary>
9+
public class LinkDropDown : ComboBox
10+
{
11+
/// <summary>
12+
/// Defines the <see cref="LinkText"/> property.
13+
/// </summary>
14+
static readonly DependencyPropertyKey LinkTextPropertyKey =
15+
DependencyProperty.RegisterReadOnly(
16+
"LinkText",
17+
typeof(string),
18+
typeof(LinkDropDown),
19+
new FrameworkPropertyMetadata(string.Empty));
20+
21+
/// <summary>
22+
/// Defines the <see cref="Header"/> property.
23+
/// </summary>
24+
public static readonly DependencyProperty HeaderProperty =
25+
HeaderedItemsControl.HeaderProperty.AddOwner(
26+
typeof(LinkDropDown),
27+
new FrameworkPropertyMetadata(typeof(LinkDropDown), HeaderChanged));
28+
29+
/// <summary>
30+
/// Defines the readonly <see cref="LinkText"/> property.
31+
/// </summary>
32+
public static readonly DependencyProperty LinkTextProperty =
33+
LinkTextPropertyKey.DependencyProperty;
34+
35+
/// <summary>
36+
/// Initializes static members of the <see cref="LinkDropDown"/> class.
37+
/// </summary>
38+
static LinkDropDown()
39+
{
40+
DefaultStyleKeyProperty.OverrideMetadata(
41+
typeof(LinkDropDown),
42+
new FrameworkPropertyMetadata(typeof(LinkDropDown)));
43+
}
44+
45+
/// <summary>
46+
/// Gets or sets a header to use as the link text when no item is selected.
47+
/// </summary>
48+
public object Header
49+
{
50+
get { return GetValue(HeaderProperty); }
51+
set { SetValue(HeaderProperty, value); }
52+
}
53+
54+
/// <summary>
55+
/// Gets the text to display in the link.
56+
/// </summary>
57+
public string LinkText
58+
{
59+
get { return (string)GetValue(LinkTextProperty); }
60+
private set { SetValue(LinkTextPropertyKey, value); }
61+
}
62+
63+
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
64+
{
65+
UpdateLinkText();
66+
}
67+
68+
private static void HeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
69+
{
70+
var source = (LinkDropDown)d;
71+
source.UpdateLinkText();
72+
}
73+
74+
private void UpdateLinkText()
75+
{
76+
if (SelectedItem != null)
77+
{
78+
var item = SelectedItem;
79+
80+
// HACK: The correct way to do this is to use a ContentPresenter in the control
81+
// template to display the link text and do a:
82+
//
83+
// ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
84+
//
85+
// to correctly display the DisplayMemberPath. However I couldn't work out how
86+
// to do it like this and get the link text looking right. This is a hack that
87+
// will work as long as DisplayMemberPath is just a property name, which is
88+
// all we need right now.
89+
if (string.IsNullOrWhiteSpace(DisplayMemberPath))
90+
{
91+
LinkText = item.ToString();
92+
}
93+
else
94+
{
95+
var property = item.GetType().GetProperty(DisplayMemberPath);
96+
LinkText = property?.GetValue(item)?.ToString();
97+
}
98+
}
99+
else
100+
{
101+
LinkText = Header.ToString();
102+
}
103+
}
104+
}
105+
}

src/GitHub.UI/GitHub.UI.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
<ItemGroup>
8080
<Compile Include="Behaviours\ClosePopupAction.cs" />
8181
<Compile Include="Behaviours\OpenPopupAction.cs" />
82+
<Compile Include="Controls\ComboBoxes\LinkDropDown.cs" />
8283
<Compile Include="Controls\Octicons\OcticonPaths.Designer.cs">
8384
<AutoGen>True</AutoGen>
8485
<DesignTime>True</DesignTime>

src/GitHub.VisualStudio/GitHub.VisualStudio.csproj

+4
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,10 @@
378378
<SubType>Designer</SubType>
379379
<Generator>MSBuild:Compile</Generator>
380380
</Page>
381+
<Page Include="Styles\LinkDropDown.xaml">
382+
<Generator>MSBuild:Compile</Generator>
383+
<SubType>Designer</SubType>
384+
</Page>
381385
<Page Include="Styles\Buttons.xaml">
382386
<SubType>Designer</SubType>
383387
<Generator>MSBuild:Compile</Generator>

src/GitHub.VisualStudio/SharedDictionary.xaml

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<cache:SharedDictionaryManager Source="pack://application:,,,/GitHub.VisualStudio;component/Styles/Buttons.xaml" />
1313
<cache:SharedDictionaryManager Source="pack://application:,,,/GitHub.VisualStudio;component/Styles/GitHubTabControl.xaml" />
1414
<cache:SharedDictionaryManager Source="pack://application:,,,/GitHub.VisualStudio;component/Styles/TextBlocks.xaml" />
15+
<cache:SharedDictionaryManager Source="pack://application:,,,/GitHub.VisualStudio;component/Styles/LinkDropDown.xaml" />
1516
</ResourceDictionary.MergedDictionaries>
1617

1718
<Style x:Key="VSStyledButton" BasedOn="{StaticResource VsButtonStyleKey}" TargetType="{x:Type Button}" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
xmlns:cache="clr-namespace:GitHub.VisualStudio.Helpers"
4+
xmlns:ui="clr-namespace:GitHub.UI;assembly=GitHub.UI">
5+
6+
<ResourceDictionary.MergedDictionaries>
7+
<cache:SharedDictionaryManager Source="pack://application:,,,/GitHub.UI;component/SharedDictionary.xaml" />
8+
</ResourceDictionary.MergedDictionaries>
9+
10+
<Style x:Key="HyperLinkToggleButton" TargetType="ToggleButton">
11+
<Setter Property="Template">
12+
<Setter.Value>
13+
<ControlTemplate TargetType="ToggleButton">
14+
<TextBlock>
15+
<TextBlock.Style>
16+
<Style TargetType="TextBlock">
17+
<Style.Triggers>
18+
<MultiDataTrigger>
19+
<MultiDataTrigger.Conditions>
20+
<Condition Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true" />
21+
<Condition Binding="{Binding Path=IsEnabled, RelativeSource={RelativeSource Self}}" Value="true" />
22+
<Condition Binding="{Binding Path=IsChecked, RelativeSource={RelativeSource TemplatedParent}}" Value="false" />
23+
</MultiDataTrigger.Conditions>
24+
<MultiDataTrigger.Setters>
25+
<Setter Property="TextDecorations" Value="Underline" />
26+
<Setter Property="FrameworkElement.Cursor" Value="Hand" />
27+
</MultiDataTrigger.Setters>
28+
</MultiDataTrigger>
29+
<DataTrigger Binding="{Binding Path=IsPressed, RelativeSource={RelativeSource TemplatedParent}}" Value="true">
30+
<Setter Property="TextDecorations" Value="Underline" />
31+
<Setter Property="FrameworkElement.Cursor" Value="Hand" />
32+
</DataTrigger>
33+
</Style.Triggers>
34+
<Setter Property="Cursor" Value="Hand" />
35+
</Style>
36+
</TextBlock.Style>
37+
<Run Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
38+
<Polygon Margin="2,0,0,1"
39+
Fill="{TemplateBinding Foreground}"
40+
Points="0,0 8,0 4,4 0,0"/>
41+
</TextBlock>
42+
</ControlTemplate>
43+
</Setter.Value>
44+
</Setter>
45+
</Style>
46+
47+
<Style TargetType="{x:Type ui:LinkDropDown}">
48+
<Setter Property="Foreground" Value="{DynamicResource GitHubActionLinkItemBrush}"/>
49+
<Setter Property="Template">
50+
<Setter.Value>
51+
<ControlTemplate TargetType="ui:LinkDropDown">
52+
<Grid>
53+
<ToggleButton Style="{StaticResource HyperLinkToggleButton}"
54+
Content="{TemplateBinding LinkText}"
55+
Foreground="{TemplateBinding Foreground}"
56+
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
57+
IsTabStop="False"/>
58+
<Popup Name="PART_Popup"
59+
AllowsTransparency="True"
60+
IsOpen="{TemplateBinding IsDropDownOpen}"
61+
Placement="Bottom">
62+
<Popup.Resources>
63+
<Style BasedOn="{StaticResource {x:Type ComboBoxItem}}" TargetType="{x:Type ComboBoxItem}">
64+
<Setter Property="Foreground" Value="{DynamicResource GitHubButtonForegroundBrush}" />
65+
<Setter Property="Padding" Value="3" />
66+
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
67+
</Style>
68+
</Popup.Resources>
69+
<Border Style="{DynamicResource GitHubComboBoxBorder}">
70+
<DockPanel Style="{DynamicResource GitHubComboBoxDockPanelContainer}"
71+
MinWidth="100">
72+
<ScrollViewer VerticalScrollBarVisibility="Auto">
73+
<ItemsPresenter/>
74+
</ScrollViewer>
75+
</DockPanel>
76+
</Border>
77+
</Popup>
78+
</Grid>
79+
</ControlTemplate>
80+
</Setter.Value>
81+
</Setter>
82+
</Style>
83+
84+
</ResourceDictionary>

src/GitHub.VisualStudio/UI/Views/PullRequestCreationView.xaml

+7-35
Original file line numberDiff line numberDiff line change
@@ -99,41 +99,13 @@
9999
<TextBlock Foreground="{DynamicResource GitHubVsGrayText}" Text="{Binding TargetBranch}" />
100100
</StackPanel>
101101

102-
<ui:GitHubActionLink x:Name="sourceBranch"
103-
Grid.Column="1"
104-
Margin="5,0"
105-
VerticalAlignment="Center"
106-
Content="{Binding SourceBranch.Name}"
107-
HasDropDown="True"
108-
ToolTip="Select a branch">
109-
<i:Interaction.Triggers>
110-
<i:EventTrigger EventName="Click">
111-
<ui:OpenPopupAction TargetName="branchPopup" />
112-
</i:EventTrigger>
113-
</i:Interaction.Triggers>
114-
</ui:GitHubActionLink>
115-
116-
<Popup x:Name="branchPopup"
117-
AllowsTransparency="True"
118-
Placement="Bottom"
119-
PlacementTarget="{Binding ElementName=branchSelection}"
120-
StaysOpen="False">
121-
<Border Style="{DynamicResource GitHubComboBoxBorder}">
122-
<DockPanel Width="100" Style="{DynamicResource GitHubComboBoxDockPanelContainer}">
123-
<ListBox DisplayMemberPath="Name"
124-
DockPanel.Dock="Top"
125-
ItemsSource="{Binding Branches}"
126-
SelectedItem="{Binding SourceBranch}"
127-
Style="{DynamicResource GitHubPopupThing}">
128-
<i:Interaction.Triggers>
129-
<i:EventTrigger EventName="SelectionChanged">
130-
<ui:ClosePopupAction TargetName="branchPopup" />
131-
</i:EventTrigger>
132-
</i:Interaction.Triggers>
133-
</ListBox>
134-
</DockPanel>
135-
</Border>
136-
</Popup>
102+
<ui:LinkDropDown Header="Source Branch"
103+
ItemsSource="{Binding Branches}"
104+
DisplayMemberPath="Name"
105+
SelectedItem="{Binding SourceBranch}"
106+
ToolTip="Select a branch"
107+
Margin="0,1,0,0"
108+
Grid.Column="1"/>
137109
</Grid>
138110

139111
</DockPanel>

src/GitHub.VisualStudio/UI/Views/PullRequestListView.xaml

+9-111
Original file line numberDiff line numberDiff line change
@@ -101,122 +101,20 @@
101101

102102
<WrapPanel Grid.Column="0" HorizontalAlignment="Stretch">
103103

104-
<ui:GitHubActionLink x:Name="stateSelection"
105-
Margin="5,0"
106-
VerticalAlignment="Center"
107-
ToolTip="Filter by state"
108-
Content="{Binding SelectedState}"
109-
HasDropDown="True">
110-
<i:Interaction.Triggers>
111-
<i:EventTrigger EventName="Click">
112-
<ui:OpenPopupAction TargetName="statePopup" />
113-
</i:EventTrigger>
114-
</i:Interaction.Triggers>
115-
</ui:GitHubActionLink>
116-
117-
<Popup x:Name="statePopup"
118-
AllowsTransparency="True"
119-
Placement="Bottom"
120-
PlacementTarget="{Binding ElementName=stateSelection}"
121-
StaysOpen="False">
122-
<Border Style="{DynamicResource GitHubComboBoxBorder}">
123-
<DockPanel Width="100" Style="{DynamicResource GitHubComboBoxDockPanelContainer}">
124-
<ListBox x:Name="states"
125-
DockPanel.Dock="Top"
126-
ItemsSource="{Binding States}"
127-
SelectedItem="{Binding SelectedState}"
128-
Style="{DynamicResource GitHubPopupThing}">
129-
<i:Interaction.Triggers>
130-
<i:EventTrigger EventName="SelectionChanged">
131-
<ui:ClosePopupAction TargetName="statePopup" />
132-
</i:EventTrigger>
133-
</i:Interaction.Triggers>
134-
</ListBox>
135-
</DockPanel>
136-
</Border>
137-
</Popup>
104+
<ui:LinkDropDown ItemsSource="{Binding States}"
105+
SelectedItem="{Binding SelectedState}"/>
138106

139107
<Separator Style="{StaticResource VerticalSeparator}" />
140108

141-
<ui:GitHubActionLink x:Name="assigneeSelection"
142-
Margin="5,0"
143-
VerticalAlignment="Center"
144-
ToolTip="Filter by assignee"
145-
Content="{Binding SelectedAssignee,
146-
Converter={ui:DefaultValueConverter},
147-
ConverterParameter=Assignee}"
148-
HasDropDown="True">
149-
<i:Interaction.Triggers>
150-
<i:EventTrigger EventName="Click">
151-
<ui:OpenPopupAction TargetName="assigneePopup" />
152-
</i:EventTrigger>
153-
</i:Interaction.Triggers>
154-
</ui:GitHubActionLink>
155-
156-
<Popup x:Name="assigneePopup"
157-
AllowsTransparency="True"
158-
Placement="Bottom"
159-
PlacementTarget="{Binding ElementName=assigneeSelection}"
160-
StaysOpen="False">
161-
<Border Style="{DynamicResource GitHubComboBoxBorder}">
162-
<DockPanel Width="100" Style="{DynamicResource GitHubComboBoxDockPanelContainer}">
163-
164-
<ListBox x:Name="assignees"
165-
DockPanel.Dock="Top"
166-
ItemsSource="{Binding Assignees}"
167-
SelectedItem="{Binding SelectedAssignee}"
168-
Style="{DynamicResource GitHubPopupThing}">
169-
<i:Interaction.Triggers>
170-
<i:EventTrigger EventName="SelectionChanged">
171-
<ui:ClosePopupAction TargetName="assigneePopup" />
172-
</i:EventTrigger>
173-
</i:Interaction.Triggers>
174-
</ListBox>
175-
</DockPanel>
176-
</Border>
177-
</Popup>
178-
109+
<ui:LinkDropDown Header="Assignee"
110+
ItemsSource="{Binding Assignees}"
111+
SelectedItem="{Binding SelectedAssignee}"/>
112+
179113
<Separator Style="{StaticResource VerticalSeparator}" />
180114

181-
<ui:GitHubActionLink x:Name="authorSelection"
182-
Margin="5,0"
183-
VerticalAlignment="Center"
184-
ToolTip="Filter by author"
185-
Content="{Binding SelectedAuthor,
186-
Converter={ui:DefaultValueConverter},
187-
ConverterParameter=Author}"
188-
HasDropDown="True">
189-
<i:Interaction.Triggers>
190-
<i:EventTrigger EventName="Click">
191-
<ui:OpenPopupAction TargetName="authorPopup" />
192-
</i:EventTrigger>
193-
</i:Interaction.Triggers>
194-
195-
</ui:GitHubActionLink>
196-
197-
<Popup x:Name="authorPopup"
198-
AllowsTransparency="True"
199-
Placement="Bottom"
200-
PlacementTarget="{Binding ElementName=authorSelection}"
201-
StaysOpen="False">
202-
<Border Style="{DynamicResource GitHubComboBoxBorder}">
203-
<DockPanel Width="100" Style="{DynamicResource GitHubComboBoxDockPanelContainer}">
204-
205-
<ListBox x:Name="authors"
206-
DockPanel.Dock="Top"
207-
ItemsSource="{Binding Authors}"
208-
SelectedItem="{Binding SelectedAuthor}"
209-
Style="{DynamicResource GitHubPopupThing}">
210-
<i:Interaction.Triggers>
211-
<i:EventTrigger EventName="SelectionChanged">
212-
<ui:ClosePopupAction TargetName="authorPopup" />
213-
</i:EventTrigger>
214-
</i:Interaction.Triggers>
215-
</ListBox>
216-
</DockPanel>
217-
</Border>
218-
</Popup>
219-
115+
<ui:LinkDropDown Header="Author"
116+
ItemsSource="{Binding Authors}"
117+
SelectedItem="{Binding SelectedAuthor}"/>
220118
</WrapPanel>
221119

222120
<ui:GitHubActionLink Grid.Column="1"

0 commit comments

Comments
 (0)