通常items控件的項模板 很容易 設置DataTemplate就能夠了,好比listbox 。可是在選中和失去焦點的時候 倒是Windows自帶的那種 藍色下拉框選中效果 ,爲了更改這種效果,首先嚐試定義控件的itemcontainerstyle:c#
1
2 3 <Setter Property="ItemContainerStyle"> 4 <Setter.Value> 5 <Style TargetType="ListBoxItem"> 6 <Setter Property="Background" Value="Azure"/> 7 <Style.Triggers> 8 <Trigger Property="IsSelected" Value="true"> 9 <Setter Property="Background" Value="Red"/> 10 </Trigger> 11 <Trigger Property="IsMouseOver" Value="true"> 12 <Setter Property="Background" Value="Red"/> 13 </Trigger> 14 </Style.Triggers> 15 </Style> 16 </Setter.Value> 17 </Setter>
試了下結果是費盡各類方法都沒法達到效果。最後在stackover flow上找到了一個答案,
https://stackoverflow.com/questions/5361698/wpf-trigger-for-listboxitem-isselected-not-working-for-background-property
那就是控件自己有一個默認Template wpf中全部控件都有默認Template。因爲默認控件裏也有IsSelected的trigger 優先級高於樣式外面定義的 因此致使外面的IsSelected trigger失效,因此,那就只能定義Template 才能達到效果。
the standard ControlTemplate of ListItem to define it's own triggers which seem to take precendence over triggers defined by the style
從新定義Template來達到效果:mvvm
1
2 3 <Setter Property="ItemContainerStyle"> 4 <Setter.Value> 5 <Style TargetType="ListBoxItem"> 6 <Setter Property="Background" Value="Azure"/> 7 <Setter Property="Template"> 8 <Setter.Value> 9 <ControlTemplate TargetType="ListBoxItem"> 10 <Border Name="border" BorderThickness="0" Background="Aquamarine" Margin="5" CornerRadius="5"> 11 <ContentPresenter/> 12 </Border> 13 <ControlTemplate.Triggers> 14 <Trigger Property="IsSelected" Value="true"> 15 <Setter TargetName="border" Property="Background" Value="red"/> 16 </Trigger> 17 </ControlTemplate.Triggers> 18 </ControlTemplate> 19 </Setter.Value> 20 </Setter> 21 </Style> 22 </Setter.Value> 23 </Setter>
注意 在模板內部 的trigger 是能夠經過name訪問的 好比上例的 TargetName=」border」 。 itemsTemplate不設置Background則認爲背景色爲透明 containerstyle能夠替換這部分的顏色 可是containerstyle只做用於每一個itemsTemplate的外圍區域並不能覆蓋它 之上 。還一種方式就是在merged Resources裏寫一些系統內置的神祕代碼:就是說元素繪製出來的東西遇到了下拉框選型IsSelected 的那種特定系統色後 咱們就把替換成咱們想要的顏色:ide
1 <ListBox Name="studentList" Style="{StaticResource myStyle}" ItemsSource="{Binding }" > 2 <ListBox.Resources> 3 <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red" /> 4 <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Red" /> 5 </ListBox.Resources> 6 </ListBox>
雖然看着是神祕代碼可是也不是想象的那麼神祕,經過Wpf的書上咱們就知道 只不過Key綁定了一個名爲HighlightBrushKey系統靜態變量而已,說白了仍是根據Windows主題風格來的 說不定換到哪一個主題這個顏色就不起做用了 因此仍是推薦模板的方式好點吧。經過查看SystemColors類就能夠知道還有哪些其它顏色。項目中處處用到的滾動條樣式也使用了mergedResources方式:佈局
1 <ListBox.Resources> 2 <!--滾動條總體樣式--> 3 <Style TargetType="{x:Type ScrollBar}"> 4 <Setter Property="SnapsToDevicePixels" Value="True"/> 5 <Setter Property="OverridesDefaultStyle" Value="true"/> 6 <Style.Triggers> 7 <Trigger Property="Orientation" Value="Horizontal"> 8 <Setter Property="Width" Value="Auto"/> 9 <Setter Property="Height" Value="10" /> 10 </Trigger> 11 <Trigger Property="Orientation" Value="Vertical"> 12 <Setter Property="Width" Value="10"/> 13 <Setter Property="Height" Value="Auto" /> 14 <Setter Property="Template" 15 Value="{StaticResource VerticalScrollBar}" /> 16 </Trigger> 17 </Style.Triggers> 18 </Style> 19 </ListBox.Resources>
還有這玩意兒:ItemContainerStyleSelector 你要本身寫c#代碼 繼承StyleSelector 在方法裏返回一個Style對象 用於製做那種表格的奇數行偶數行 變換樣式,在咱們項目裏因爲沒有用到 這裏只是提一下。
對於Items控件 在 Template裏 顯示定義ScrollViewer 來控制橫向 或者縱向滾動:學習
1 <Setter Property="Template"> 2 <Setter.Value> 3 <ControlTemplate> 4 <Border BorderBrush="Blue" BorderThickness="1"> 5 <ScrollViewer HorizontalScrollBarVisibility="Visible"> 6 <ItemsPresenter></ItemsPresenter> 7 </ScrollViewer> 8 </Border> 9 </ControlTemplate> 10 </Setter.Value> 11 </Setter>
WrapPanel是能夠按像素滾動的,StackPanel 按整行滾動this
Items控件是那種帶多個子項的控件 好比最多見的就是listbox Items控件的子項的呈現要想徹底更改外觀經過itemTemplate ,不然只能簡單的改改背景色之類的 仍是常見的下拉框那種外觀。
幾個重要屬性和標籤的對應關係:
itemTemple對應 Datatemple 用於定義每一個項的外觀
Temple對應ControlTemplate 用於定義大的控件外觀
<ItemsPresenter></ItemsPresenter>表明子項的內容渲染
ItemsPanel 對應ItemsPanelTemplate表明子項的容器面板
containerStyle用於控制子項的容器的外觀 上面已經說了
一個簡易的listbox的 外觀更改 和綁定示例:spa
1 <Style x:Key="myStyle" TargetType="ListBox"> 2 <Setter Property="Template"> 3 <Setter.Value> 4 <ControlTemplate> 5 <Border BorderBrush="Blue" BorderThickness="1"> 6 <ScrollViewer HorizontalScrollBarVisibility="Visible"> 7 <ItemsPresenter></ItemsPresenter> 8 </ScrollViewer> 9 </Border> 10 </ControlTemplate> 11 </Setter.Value> 12 </Setter> 13 <Setter Property="ItemTemplate"> 14 <Setter.Value> 15 <DataTemplate> 16 <Border BorderBrush="Red" BorderThickness="1" Margin="10"> 17 <TextBlock Text="{Binding Age}"></TextBlock> 18 </Border> 19 </DataTemplate> 20 </Setter.Value> 21 </Setter> 22 <Setter Property="ItemsPanel"> 23 <Setter.Value> 24 <ItemsPanelTemplate> 25 <StackPanel Orientation="Vertical" Margin="0,0,35,0" ></StackPanel> 26 </ItemsPanelTemplate> 27 </Setter.Value> 28 </Setter> 29 </Style>
還有就是ControlTemplate最好要寫上targettype 不然不少屬性trigger都會認爲無效 沒法編譯經過。注意DataTemplate也並不必定要寫在一個Style裏面 你能夠自由組合 ,在幾個控件中使用。設計
數據驅動 和樣式模板這些是Wpf的精髓,這種設計大大弱化winform裏的「自定義控件」其實就是手動渲染繪製。Wpf的基礎是 最外面爲咱們看得見的控件佈局 稱之爲logicTree 。每種控件是有一些本身的規則限定的好比button的content只能是文本。控件能夠經過屬性更改外觀 。style是屬性的打包。Style能夠繼承。每一個控件裏面能夠定義Template來徹底更改控件的外觀。Wpf已經爲咱們預先定義好了一些控件的Template ,好比button 默認的就是那種灰色的方形的樣式。WPF總體的從窗體到控件 的渲染是一個遞歸系統。logicTree是主幹 visualtree是枝葉。logicTree是固定的visualtree是不固定的 根據綁定的數據動態生成 ,說白了就是Template。有時候咱們想要自定義一個Template 老是無從着手 由於太複雜了,看着是一個按鈕 裏面由各類線條和填充構成 標籤寫錯了 wpf又不認,這時候咱們能夠經過wpf自帶的默認Template來學習 在它基礎上更改 修改。輸出默認模板的c#代碼示例,好比有個名爲btn1的button:code
1 string xamlString = XamlWriter.Save(btn1.Template); 2 FileStream fs= File.Create("aa.txt"); 3 StreamWriter sw = new StreamWriter(fs); 4 sw.Write(xamlString); 5 sw.Close();
使用下面這段代碼能夠簡單的查看一個控件的visualTree :orm
1 public void ShowVisualTree(int deepenLeve, DependencyObject refrenceObj) 2 { 3 for (int i = 0; i < deepenLeve; i++) 4 { 5 Console.Write("\t"); 6 } 7 if (refrenceObj is TextBlock) 8 { 9 TextBlock txblock = refrenceObj as TextBlock; 10 txblock.Background = Brushes.Red; 11 } 12 Console.Write(refrenceObj.ToString() + "\r\n"); 13 int childCount = VisualTreeHelper.GetChildrenCount(refrenceObj); 14 for (int i = 0; i < childCount; i++) 15 { 16 ShowVisualTree(deepenLeve + 1, VisualTreeHelper.GetChild(refrenceObj, i)); 17 } 18 }
1 ShowVisualTree(0, btn1);
運行能夠看到 一個button在win10默認風格下 由一個border一個 contentpresenter 一個textblock一層層嵌套構成。固然在這裏面你也能夠操做visualtree裏面的元素好比上面把textBlock的背景改爲了紅色。
還有須要注意的是 若是你手動寫這樣的代碼:
1 <ListBox> 2 <ListBoxItem>111</ListBoxItem> 3 <ListBoxItem>222</ListBoxItem> 4 </ListBox>
他並不能應用listbox的itemsTemplate模板 ,並不能, menuItem也是如此。你必需要定義一個targettype=ListBoxItem的模板才行,而後逐步應用 ,在WPF中這是至關複雜的,因此比較好的方式是用上面的方法 輸出visualTree,而後在基礎之上慢慢修改。
Wpf開發 時常會用到網頁應用程序的那種樣式 ,好比搜索框 在沒有輸入任何關鍵字的時候顯示 「請輸入模板名稱」 的水印 以提示用戶。實現方式:定義textbox的模板 設置觸發器 在文本框內容爲空的狀況下 設置Background爲一個帶文本的virtualBrush
1 2 <TextBox Text="dfdf" Height="30" Name="nihao" > 3 <TextBox.Style> 4 <Style TargetType="TextBox"> 5 <Setter Property="Template"> 6 <Setter.Value> 7 <ControlTemplate TargetType="TextBox"> 8 <Border BorderThickness="1" BorderBrush="Blue"> 9 <TextBlock Name="textContent" Text="{TemplateBinding Text}"></TextBlock> 10 </Border> 11 <ControlTemplate.Triggers> 12 <MultiTrigger > 13 <MultiTrigger.Conditions> 14 <Condition Property="Text" Value=""/> 15 </MultiTrigger.Conditions> 16 <Setter Property="Background" TargetName="textContent"> 17 <Setter.Value> 18 <VisualBrush AlignmentX="Left" AlignmentY="Top" Stretch="None"> 19 <VisualBrush.Visual> 20 <TextBlock Width="100" Height="20">請輸入內容</TextBlock> 21 </VisualBrush.Visual> 22 </VisualBrush> 23 </Setter.Value> 24 </Setter> 25 </MultiTrigger> 26 </ControlTemplate.Triggers> 27 </ControlTemplate> 28 </Setter.Value> 29 </Setter> 30 </Style> 31 </TextBox.Style> 32 </TextBox>
注意 通常咱們應用trigger 都是應用到style.triggers層, 其實也可應用到ControlTemplate層 只不過setter的時候你須要改變哪個元素 你要指定targetName 不然wpf沒法識別。實際上是同樣的 你應用到哪一層則在哪一層起做用。上面實現了一個簡易的水印。就像上面的若是trigger是style層的天然就是更改的最外層應用style的textbox的Background 。一個checkBox的自定義樣式:
1 <CheckBox > 2 <CheckBox.Template> 3 <ControlTemplate TargetType="CheckBox"> 4 <StackPanel Orientation="Horizontal"> 5 <Rectangle Name="breakRectangle" Stroke="Red" StrokeThickness="2" Width="20" Height="20"> 6 <Rectangle.Fill> 7 <SolidColorBrush Color="Azure"></SolidColorBrush> 8 </Rectangle.Fill> 9 </Rectangle> 10 <ContentPresenter></ContentPresenter> 11 </StackPanel> 12 <ControlTemplate.Resources> 13 <SolidColorBrush x:Key="redBrush" Color="Red"/> 14 </ControlTemplate.Resources> 15 <ControlTemplate.Triggers> 16 <Trigger Property="IsChecked" Value="True"> 17 <Setter TargetName="breakRectangle" Property="Fill" Value="{StaticResource ResourceKey=redBrush}"></Setter> 18 </Trigger> 19 </ControlTemplate.Triggers> 20 </ControlTemplate> 21 </CheckBox.Template> 22 複選框自定義模板 23 </CheckBox>
咱們寫{bind} 其實就是用一個binding類對象做爲依賴屬性的值 ,沒什麼好神奇的 。只不過大括號這種{bind } 是wpf自帶的使用內置的converter 就能夠轉換解析 而後在UI上顯示出對應的東西。跟Asp.Net裏同樣 綁定 無非就肯定三個東西 ,1數據源 2綁數據源的哪一個字段 也就是path。 3綁到UI的什麼地方 。
第1,2步:
1 Binding binding = new Binding("Value") { Source = this.myslider };
其中Value是綁的哪一個字段也就是path ,Source表明數據源 。
第3步:
1 this.myTxt.SetBinding(TextBox.TextProperty, binding);
表明文本框的text屬性 來顯示 myslider的Value
對於日常業務的狀況其實咱們大都是直接這樣寫:
1 <TextBox Text="{Binding Name}"></TextBox>
對於1 數據源 其實在mvvm模式頁面載入的時候就肯定了 ,應用binding的時候會自動去找控件自己的DataContext做爲數據源 找不到再去找頁面this.DataContext 做爲數據源。對於3 綁定到UI的什麼地方 Text="" 就足以說明了 。因此直接<TextBox Text="{Binding Name}"></TextBox> 表明綁定當前數據上下文的Name字段到文本框的文本顯示。每一個控件自己都有個dataContext 表明數據上下文 ,可是對於Items控件日常咱們都沒有去設置他。只設置ItemsSource 就能夠了 。ItemsSource表明控件自身的子項綁定時的數據源 爲一個集合。在控件的DataTemple的時候自動的以集合的單個元素爲輸出 好比itemsSource=newList<Person>() 那麼dataTemplate裏一個文本框 text=」{binding Name}」則綁定的是每一個person.Name 屬性。 若是自身沒有設置dataContext 會自動從該控件的上一級去尋找 。最終從整個窗體的根部也就是this.dataContext 尋找數據綁定對象 若是再沒有那窗體上就不會有任何項目顯示。 題外話:這都是依賴屬性的功勞,依賴屬性能夠向上一級一級的造成binding鏈,依賴屬性的精髓是「依賴」 ,神奇之處在於我身上顯示的這個值能夠是我本身的也能夠不是我本身的 別人變了我還能獲得實時更新,說白了就是你搞這種屬性 好處就是他的值能夠應用{binding} Wpf的UI屬性大都是依賴屬性,咱們日常寫個Person類須要讓他動態綁動態更新麼 不須要 ,固然你要他動態更新綁定更新也是能夠的 雖然不是UI對象,只要你有業務需求。可是日常業務中咱們用的也並很少 都是使用繼承NotifyPropertyChanged 的方式。默認綁定上去就是雙向的。對於綁定集合 ,在增長項 刪除項的時候 界面上並不會自動變更 最好使用ObservableCollection。對於converter我的以爲平時用的並很少。綁定的時候也能夠用format 來格式化:
1 Text="{Binding StringFormat=共{0}個模板,ElementName=moduleList,Path=Items.Count}"
另外 綁定元素也能夠有多種靈活的運用。下面這種經過listBox有沒有數據項來決定 元素的啓用與禁用:
1 IsEnabled="{Binding ElementName=stuctList,Path=HasItems}"
還能夠這樣:
1 Text="{Binding ElementName=stuctList,Path=SelectedItem.Name}"
直接顯示選中項的數據對象的字段值。
把某個相對元素的屬性綁定到本元素:
1 Text="{Binding RelativeSource={RelativeSource AncestorType=StackPanel,AncestorLevel=1},Path=Name}"
在wpf中普通的Image對象是 不會發生圖片模糊的狀況的,只須要設置 Stretch="None" 。圖片即會按照原始大小顯示。可是若是佈局複雜了,最簡單的就是 在外面套一個border元素 ,程序運行時圖片出現模糊,準確的說是圖片發虛 尤爲是有線條輪廓的那種圖片。解決方式: 爲外層的元素添加 UseLayoutRounding="True"。 Wpf界面是矢量的 UseLayoutRounding爲true 元素在渲染時會進行像素對齊。
還有就是設置半透明背景 只須要設置Background值爲argb形式就能夠了 ,若是設置opticity的話整個元素都會變爲半透明。遮罩彈出層其實也很簡單 最外圍使用grid包起來 彈出層整個一個半透明grid層 裏面包着UI Grid默認具備鋪滿布局的效果,grid裏面的控件若是不設置 row 和col 默認具備重疊佈局 也就是一個控件能夠重疊在另外一個上面,由此能夠達到遮罩彈出層的效果。
1 <Grid> 2 <Button HorizontalAlignment="Left" VerticalAlignment="Top" Width="80" Height="30" Click="Button_Click_1"> 打開彈出層</Button> 3 <Grid Background="#7F000000" Name="grid1" > 4 <Button Width="80" Height="30" Click="Button_Click" >關閉彈出層</Button> 5 </Grid> 6 </Grid>
在按鈕事件裏設置 grid1的Visible屬性 可進行彈出和關閉。
在易電助手3.0項目中咱們須要根據右鍵點的項動態更改右鍵菜單哪一個顯示與不顯示
咱們通常設置一個ContextMenu 爲資源 默認模式是共享模式 就是說這個地方變了另一個地方也變了。
設置ItemsTemplate裏的最大的元素的 ContextMenu爲指定菜單就能夠了 ,而後在PreviewMouseRightButtonDown事件裏 找到元素 的DataContext 即找到了當前點的是哪一個數據對象
1 Border curCtr = sender as Border; 2 if (curCtr == null) 3 return; 4 var currentStruct = curCtr.DataContext as ModuleStruct;
還有咱們在非button控件設置鼠標左鍵雙擊事件的方式爲 設置 PreviewMouseLeftButtonDown事件。而後在代碼裏判斷 e.ClickCount>=2 則爲雙擊。
在控件上掛載依賴屬性有時候能夠達到一些意想不到的效果,而且這個依賴屬性能夠不是他的父元素控件的值:
1 <Button Height="30" local:ControlHelper.Angle="10" Width="50" > 2 <Button.Template> 3 <ControlTemplate> 4 <Border BorderThickness="1" BorderBrush="Blue"> 5 <TextBox Text="{Binding RelativeSource={RelativeSource TemplatedParent},Path=(local:ControlHelper.Angle)}"></TextBox> 6 </Border> 7 </ControlTemplate> 8 </Button.Template> 9 </Button>
固然在頁面上你必須引入local的命名空間 而且在controlHelper裏實現Angle這個依賴屬性:
1 xmlns:local="clr-namespace:WpfApplication2"
1 public class ControlHelper : DependencyObject 2 { 3 public static int GetAngle(DependencyObject obj) 4 { 5 return (int)obj.GetValue(AngleProperty); 6 } 7 public static void SetAngle(DependencyObject obj, int value) 8 { 9 obj.SetValue(AngleProperty, value); 10 } 11 12 // ... 13 public static readonly DependencyProperty AngleProperty = 14 DependencyProperty.RegisterAttached("Angle", typeof(int), typeof(ControlHelper), new PropertyMetadata(0, OnAngleChanged)); 15 16 17 private static void OnAngleChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) 18 { 19 var element = obj as UIElement; 20 if (element != null) 21 { 22 element.RenderTransformOrigin = new Point(0.5, 0.5); 23 element.RenderTransform = new RotateTransform(double.Parse(e.NewValue.ToString())); 24 } 25 } 26 27 }
上面爲控件添加了angle屬性來達到控件旋轉的效果。