在silverlight 3 時必需要寫javascript才能做到右鍵選單,到4的時候省了javascript這一步驟,但後序的處理,還沒有像window form可以拉拉control就可以做到,要自己處理popup的open、close、選單的樣式、事件等等,很麻煩,也應此各式各樣第三方套件就出來了,像silverlight 4 toolkit中的contextmenuservice,不過呢,小弟想自己寫寫做練習,也就有了本篇。
在silverlight 3 時必需要寫javascript才能做到右鍵選單,到4的時候省了javascript這一步驟,但後序的處理,還沒有像window form可以拉拉control就可以做到,要自己處理popup的open、close、選單的樣式、事件等等,很麻煩,也應此各式各樣第三方套件就出來了,像silverlight 4 toolkit中的contextmenuservice,不過呢,小弟想自己寫寫做練習,也就有了本篇。
右鍵事件
silverlight 4 時新增了二個右鍵事件
mouserightbuttondown
mouserightbuttonup
其中只有在mouserightbuttondown時handled,silverlight原本的選單才不會出現。
圖一 在mouserightbuttondown時handled的效果。
contextmenu基類
我用silverlight text editor sample的contextmenu的基類,它有一個popup,內容有一個滿版透明的canvas當contextmenu的容器,在顯示的時候,點擊contextmenu外時,結束popup。
圖二 contextmenu的基類
圖三 popup機制,最上層為contextmenu、中層透明的canvas、下層才是原畫面
樣式
在app.xaml中加入二個Resource。
ContextMenuButton為每一個Buttion的樣式,為滑鼠移入有黃色效果。
<style x:key="contextmenubutton" targettype="button">
<setter property="background" value="#ff1f3b53"/>
<setter property="foreground" value="#ff000000"/>
<setter property="padding" value="3"/>
<setter property="borderthickness" value="1"/>
<setter property="borderbrush">
<setter.value>
<lineargradientbrush endpoint="0.5,1" startpoint="0.5,0">
<gradientstop color="#ffa3aeb9" offset="0"/>
<gradientstop color="#ff8399a9" offset="0.375"/>
<gradientstop color="#ff718597" offset="0.375"/>
<gradientstop color="#ff617584" offset="1"/>
</lineargradientbrush>
</setter.value>
</setter>
<setter property="template">
<setter.value>
<controltemplate targettype="button">
<grid>
<visualstatemanager.visualstategroups>
<visualstategroup x:name="commonstates">
<visualstate x:name="normal"/>
<visualstate x:name="mouseover">
<storyboard>
<doubleanimationusingkeyframes storyboard.targetproperty="(uielement.opacity)" storyboard.targetname="background">
<easingdoublekeyframe keytime="0" value="1"/>
</doubleanimationusingkeyframes>
</storyboard>
</visualstate>
<visualstate x:name="pressed">
<storyboard>
<coloranimationusingkeyframes storyboard.targetproperty="(border.background).(solidcolorbrush.color)" storyboard.targetname="background">
<splinecolorkeyframe keytime="0" value="#ffffe575"/>
</coloranimationusingkeyframes>
<doubleanimationusingkeyframes storyboard.targetproperty="(uielement.opacity)" storyboard.targetname="background">
<easingdoublekeyframe keytime="0" value="1"/>
</doubleanimationusingkeyframes>
<doubleanimationusingkeyframes storyboard.targetproperty="(uielement.opacity)" storyboard.targetname="grid">
<easingdoublekeyframe keytime="0" value="0"/>
</doubleanimationusingkeyframes>
<coloranimationusingkeyframes storyboard.targetproperty="(border.borderbrush).(solidcolorbrush.color)" storyboard.targetname="background">
<easingcolorkeyframe keytime="0" value="#ffc28a30"/>
</coloranimationusingkeyframes>
</storyboard>
</visualstate>
<visualstate x:name="disabled">
<storyboard>
<doubleanimationusingkeyframes storyboard.targetproperty="opacity" storyboard.targetname="disabledvisualelement">
<splinedoublekeyframe keytime="0" value=".55"/>
</doubleanimationusingkeyframes>
</storyboard>
</visualstate>
</visualstategroup>
<visualstategroup x:name="focusstates">
<visualstate x:name="focused"></visualstate>
<visualstate x:name="unfocused"/>
</visualstategroup>
</visualstatemanager.visualstategroups>
<border x:name="background" borderthickness="{templatebinding borderthickness}" background="#fffdf7e2" cornerradius="3" borderbrush="#ffdbce99" opacity="0">
<grid x:name="grid" margin="1">
<grid.background>
<lineargradientbrush endpoint="0.5,1" startpoint="0.5,0">
<gradientstop color="white" offset="0"/>
<gradientstop color="#ffffe08d" offset="0.924"/>
<gradientstop color="#ffffd76e" offset="0.5"/>
<gradientstop color="#fffff0c0" offset="0.484"/>
<gradientstop color="#fffff0c7" offset="1"/>
</lineargradientbrush>
</grid.background>
</grid>
</border>
<contentpresenter x:name="contentpresenter" contenttemplate="{templatebinding contenttemplate}" content="{templatebinding content}" horizontalalignment="{templatebinding horizontalcontentalignment}" margin="{templatebinding padding}" verticalalignment="{templatebinding verticalcontentalignment}"/>
<rectangle x:name="disabledvisualelement" fill="#ffffffff" ishittestvisible="false" opacity="0" radiusy="3" radiusx="3"/>
<rectangle x:name="focusvisualelement" ishittestvisible="false" margin="1" opacity="0" radiusy="2" radiusx="2" stroke="#ff6dbdd1" strokethickness="1"/>
</grid>
</controltemplate>
</setter.value>
</setter>
</style>
ListBoxContextMenu為在ListBox上按右鍵時的效果。
<DataTemplate x:Key="ListBoxContextMenu">
<Border BorderThickness="1" BorderBrush="#FFA7ABB0" Background="White">
<Grid Margin="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25" />
<ColumnDefinition Width="105" />
</Grid.ColumnDefinitions>
<Rectangle Fill="#FFE9EEEE" />
<Rectangle Fill="#FFE2E4E7" HorizontalAlignment="Right" Width="1" />
<!--Insert-->
<Button x:Name="InsertButton" Height="22" Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Top" HorizontalContentAlignment="Left" Grid.ColumnSpan="2" Style="{StaticResource ContextMenuButton}" >
<StackPanel Orientation="Horizontal">
<Image Source="/ContentMenuDemo;Component/Images/Insert.png" Height="16" Width="16" HorizontalAlignment="Left" Margin="1,0,0,0"/>
<TextBlock Text="Insert" HorizontalAlignment="Left" Margin="16,0,0,0"/>
</StackPanel>
</Button>
<!--Edit-->
<Button x:Name="EditButton" Height="22" Margin="0,24,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Top" HorizontalContentAlignment="Left" Grid.ColumnSpan="2" Style="{StaticResource ContextMenuButton}" >
<StackPanel Orientation="Horizontal">
<Image Source="/ContentMenuDemo;Component/Images/Edit.png" Height="16" Width="16" HorizontalAlignment="Left" Margin="1,0,0,0"/>
<TextBlock Text="Edit" HorizontalAlignment="Left" Margin="16,0,0,0"/>
</StackPanel>
</Button>
<!--Delete-->
<Button x:Name="DeleteButton" Height="22" Margin="0,48,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Top" HorizontalContentAlignment="Left" Grid.ColumnSpan="2" Style="{StaticResource ContextMenuButton}" >
<StackPanel Orientation="Horizontal">
<Image Source="/ContentMenuDemo;Component/Images/Delete.png" Height="16" Width="16" HorizontalAlignment="Left" Margin="1,0,0,0"/>
<TextBlock Text="Detele" HorizontalAlignment="Left" Margin="16,0,0,0"/>
</StackPanel>
</Button>
</Grid>
</Border>
</DataTemplate>
寫成DataTemplate是方便程式中載入。
實作
新增ListBoxContextMenu類,繼承ContextMenu,實作虛擬函式GetContent
protected override FrameworkElement GetContent()
{
//將內容用DataTemplate方式寫在Resources中。
//因為DataTemplate沒有辦法加入事件,所以讀出來後在添加。
FrameworkElement template = (Application.Current.Resources["ListBoxContextMenu"] as DataTemplate).LoadContent() as FrameworkElement;
//為每個Button增加對應的事件處理函式
(template.FindName("InsertButton") as Button).Click += new RoutedEventHandler(OnInsertButtonClick);
(template.FindName("EditButton") as Button).Click += new RoutedEventHandler(OnEditButtonClick);
(template.FindName("DeleteButton") as Button).Click += new RoutedEventHandler(OnDeleteButtonClick);
return template;
}
private void OnListBoxMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
ListBoxContextMenu cm = new ListBoxContextMenu(this.DemoListBox, e);
cm.Show();
}
這純粹是學習所以才自己寫寫看,真正開發還是用Toolkit比較好。
參考資料
silverlight text editor sample