|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Services
Chapters
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
Article Rewritten 21 Feb 2008On 21 Feb 2008, this article was rewritten. The main reason for this was because my original approach was to try and keep everything as simple as possible. As I was writing part two of this series, I realized that while simple was good, there were a better "long term" methods to style and skin management. I will cover several different techniques that can be used including the one I've chosen to implement. I had a choice to make. Leave this article as is and just change the future articles, or spend the time and come back and rewrite this article with the better, "long term" methods I'll be describing here. I chose to rewrite this article so that the series would have continuity and flow between articles. IntroductionThis is the first in a series of articles on writing WPF Business Applications in VB.NET using Visual Studio 2008. This series will conclude with a complete business application that utilizes all the features presented here and from some of my other Code Project articles. I will also include an ASP.NET web site that consumes the data using the same business layer we will build. For the purpose of this series, a business application is defined as form based data entry, data processing and reporting application. This series targets business application developers who know the basics of WPF or have experience with WPF. The primary focus of the series is on writing business applications, rather than addressing the question, "what is a style, how do I apply a style or how does the There are many fine WPF authors here on Code Project with great insight and code. If you are new to WPF can I suggest that you bookmark this article and take the time to read the foundational tutorials that Code Project MVP's Josh Smith and Sacha Barber have authored? In addition to their tutorials, each have many WPF articles here on Code Project and their blogs with sample code. At the bottom of this article I have also listed the WPF books that I have purchased and use. I should also mention that the SDK Team has put a lot of work into providing a lot of good, simple examples in the WPF SDK that are easy to learn from. When you installed Visual Studio 2008, this was also installed. Take advantage of this free code. Each of the components that are presented in this series will be package so that you can use them by simply referencing them in your applications. Each of the articles will build on previous articles. When necessary, the articles will have one or more Silverlight Streaming videos to enhance the learning experience. Series BackgroundI'm starting this series because I believe that WPF will soon become the mainstream platform for new business applications. Many developers transitioning from other form based platforms have a lot to learn before they can program or design WPF applications effectively. I've been reading questions like these on various forums, "how do I structure my application; how do I change the skin at run-time; how do I validate my users input; where do I validate my users input; how does WPF implement MDI; how do I setup application navigation in a business application; ..." These are very good questions and deserve an answer. If you have additional questions, please post them as comments to this article and I'll try and answer them in future articles with examples. Another reason for this series is I want to publish my ideas and techniques and have them critiqued, and then readers and I can grow from the input as WPF developers. This is the heart beat of Code Project; to share and learn from one another. You will notice throughout this series, that I try to do everything as simply as possible. Small software vendors that deliver business applications typically do not have the resources of larger shops. Smaller shops need to be able to get their bullet proof, feature rich applications to market in a timely manner. So they must put their limited resources into developing maintainable solid application features as opposed to designing the perfect application framework. Here is the good news. The knowledge and experience that you bring from WinForms, ASP.NET or other forms based platforms is valid and you can draw extensively from it. Personally, I'm an ASP.NET guy who has added WPF to my skill set. I hope this series will allow developers to, "connect the dots" so to speak and write great business applications. WPF Business Application SeriesI have a few other ideas but for now the road map is:
Solution StructureStructuring a business application solution is very straightforward. The method I'm showing here is how I actually structure my "real world" applications. The top Core assembly is where all non-business related middle tier objects reside. For example, custom controls, validation, logging, messaging, data access, helper functions, etc. All projects in the solution, reference Core.dll. Each of the root folders in Core represents a Namespace. The Here is a tip on how to save yourself a lot of grief when you create your own Core (or similar) project. Ensure that you use the, "WPF Custom Control Library" project template as opposed to the "Class Library" template when you add your Core project to your assembly. This adds all the required references, and makes available additional, "New Item..." templates in Visual Studio 2008, like the, "WPF CustomControl" template. The Core assembly is typically only modified by the senior developers. This assembly does not change very often. This .dll is normally referenced by developers and not part of their visible source code. For this series, the Core assembly will be part of our source code so that we can learn about it. Doing this also shortens build times for developers. Notice the The The The project Once we get deeper into the series, you will see that each business form is actually a WPF The I've placed each skin in its own folder because as time goes on, you may need to add more resource dictionaries for each skin, additionally you may want to have different images for each skin. To implement different images for each skin, just assign each image a resource name and use that resource in your code when you want to reference that image. This is a decision you will want to make up front so that you don't have to go back and rework your application. I always name my main window, Business Application SkinningThis is one section of the article that after the initial writing I came back and rewrote completely. I did this after getting some suggestions, corresponding with an application skin vendor and speaking with Josh Smith. I realized in my effort to keep everything simple, I was missing the larger WPF picture of Styling and ControlTemplates. So rather than shying away from creating a good number of styles, we will be embracing the use of this great WPF feature. WPF developers need to learn to structure their applications so that it's rendering can be altered by applying a style or a new control template without effecting the operation of the application. These styles or control templates can be designed by the developer, designer or purchased from 3rd party vendors. The concept of styling the UI as opposed to setting properties on UI controls can take some getting used to if you have not done this before. This is actually part of the WPF learning curve. WPF allows the styling without the UI markup even knowing that it's being styled. WPF styles can be explicitly assigned or globally applied by control Type. Learning to take advantage of this platform feature will make the developers life simpler and easier in the long run. There are several techniques for skinning your application. Let's have a look at them. Hey, My Application Only Has/Needs One Color SchemeIf you are delivering an application that will only have one color scheme, you may be tempted to set the colors directly on the controls just like WinForms or ASP.NET without CSS. Think long and hard before heading down this bumpy road. Your application will become very difficult to maintain over time and if a change is required you may be required to touch all the forms, in other words, touching the markup. If your application falls into this category, you will benefit greatly by following the below or similar suggestions. Application maintenance is much easier and one day you may want to have a designer give your UI a few finishing touches. With the styling of the application cleanly separated from the actual UI, this task can very easily be accomplished without getting into your UI markup at all. Applying Resources to Control PropertiesThis is the technique I was using the first article. It is by far the simplest method of skinning, but harder to maintain over the long haul. When using this technique, you are assigning resources to one or more properties on the controls. For example, you may assign the On the surface this sounds good, but by having to set multiple properties on each control, this makes application development harder, since these same properties would have to be set on similar controls on other forms. Then, what happens if the boss wants the Assigning Styles to ControlStyles can be explicitly assigned to a control by a assigning a resource Run the demo program. Notice the nice custom look for the <TabControl
x:Name="tcOpenPages" SelectedIndex="0" TabStripPlacement="Bottom"
Style="{DynamicResource TabControlStyle_Application}" >
<TabItem Style="{DynamicResource TabItemStyle_Application}" Header="Part 1">
<demo:PartOne />
</TabItem>
</TabControl>
Styles can also be applied globally to a control Type. This below style in a skin resource dictionary does not have a This global <Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate" Value="{DynamicResource validationTemplate}" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding Path=(Validation.Errors)[0].ErrorContent,
RelativeSource={x:Static RelativeSource.Self}}" />
</Trigger>
</Style.Triggers>
</Style>
Purchase 3rd Party Styles and Control TemplatesAnother option you have is to purchase a 3rd party vendor theme pack or control template package. In fact Josh Smith has a blog entry Buying Themes for WPF Applications that brings this option to light. Two vendors that have started in this market are Reuxables and Infragistics. While neither vendor has a full blown solution, with every WPF control styled in the theme pack, Josh and I believe a comprehensive solution will be available in the future. The Reuxables theme pack covers a good number of controls. You don't have to touch your markup at all. Just load the resource dictionary and watch your application take on a new look. They have a free download that you can use in your projects to test with. Ultimately, we will probably see developers using a hybrid of 3rd party styles and application developer styles being used in an application. Skinning This ApplicationAll skins files must have the same resource keys. Styles do not require keys, but if assigned, then each skin much have a style with that The method for loading skins used in this application is not the only way this can be accomplished. Skin resource dictionaries can be loaded from loose XAML files or from other satellite assemblies. Take your application requirements into consideration and find the simplest method to meet your specification requirements and use that method. If your application requires that customers be able to design their own skins without your knowledge or intervention, then allow for the loading of loose XAML files. If you want to be able to distribute a new skin without recompiling your application or redistributing the entire application, then use the dynamic loading of satellite assembly method and just distribute a new satellite assembly to your customers. When your application references a style in a skin resource dictionary, you must always reference the style as a
FYI: Karl already knows that his "design" skills are not his strongest skill, so go easy on me. This simple application allows you to change the settings on each To change the skin use the I like using Microsoft Blend 2, December Preview to adjust my colors and styles because it allow you to visually edit a styles and see those changes real-time on your business form. Making the second and third skins only took about 20 minutes each. Thanks to WPF, this type of UI restyling is just a few simple lines of code. Let me say something about skinning. You can do MUCH more than simple color changes. You can change entire control template, the rendering of the control, etc. Since we are writing a business application, I'm sticking with color changes and a few simple control template changes, but you are only limited by your imagination, time and skills. Select Skin CodeI learned a good bit from Josh Smith's Creating A Skinned User Interface In WPF article on skinning UI's. This application goes a step further with respect to managing the swapping out of the resource dictionaries. The code ensure that if you have multiple resource dictionaries loaded, only the skin dictionary will be swapped, the other resource dictionaries will not be touched. The below code demonstrations one technique for swapping skin dictionaries that works pretty good and it's very simple. This simple code handles the menu click events. Class ApplicationMainWindow
#Region " Methods "
Private Sub OnViewMenuItemClick(ByVal sender As Object, _
ByVal e As RoutedEventArgs)
Dim objClicked As MenuItem = CType( _
e.OriginalSource, MenuItem)
For Each mi As MenuItem In CType(sender, _
MenuItem).Items
mi.IsChecked = mi Is objClicked
Next
CType(Application.Current, Application).ApplySkin( _
New Uri(TryCast(objClicked.CommandParameter, String), _
UriKind.Relative))
End Sub
Private Sub OnMenuItemExitClick(ByVal sender As System.Object, _
ByVal e As System.Windows.RoutedEventArgs)
Me.Close()
End Sub
#End Region
End Class
When user selects a skin from the In this implementation, the image location is stored in the Apply Skin CodeImports System.Collections.ObjectModel
''' <summary>
''' This code supports having one ResourceDictionary for each skin.
''' If your application will have more than one, you'll need to
''' modify this code to use collections instead of a single
''' resource dictionary.
''' </summary>
Class Application
Private _objLoadedSkinResourceDictionary As ResourceDictionary
Public ReadOnly Property MergedDictionaries() As Collection(Of ResourceDictionary)
Get
Return MyBase.Resources.MergedDictionaries
End Get
End Property
Private Sub Application_Startup(ByVal sender As Object, ByVal e As _
System.Windows.StartupEventArgs) Handles Me.Startup
For Each obj As ResourceDictionary In Me.MergedDictionaries
If obj.Source IsNot Nothing AndAlso obj.Source.OriginalString.Contains( _
"\Skins\") Then
_objLoadedSkinResourceDictionary = obj
'only have one to load up so we are out of here
Exit Sub
End If
Next
End Sub
Public Sub ApplySkin(ByVal objSkinDictionaryUri As Uri)
If String.IsNullOrEmpty(objSkinDictionaryUri.OriginalString) Then
Throw New NullReferenceException( _
"The skin dictionary URI OriginalString was null or empty.")
End If
Dim objNewSkinDictionary As ResourceDictionary = TryCast( _
Application.LoadComponent(objSkinDictionaryUri), ResourceDictionary)
If objNewSkinDictionary Is Nothing Then
Throw New NullReferenceException(String.Format( _
"The {0} ResourceDictionary could not be loaded.", _
objSkinDictionaryUri.OriginalString))
End If
Me.MergedDictionaries.Remove(_objLoadedSkinResourceDictionary)
_objLoadedSkinResourceDictionary = objNewSkinDictionary
Me.MergedDictionaries.Add(objNewSkinDictionary)
End Sub
End Class
First, this code supports having one resource dictionary define a skin. If you need to have multiple resource dictionaries define your styles, then this code must be modified to support the selecting, adding, removing and tracking of multiple dictionaries per skin. The easiest way to do this would be to use the generic The purpose of the I chose to use the simplest method I could come up with to accomplish this task. When the application starts up, find the resource dictionary that was assigned in the When the Some would ask why you didn't utilize the look for string " As you can see, this code is very simple, can be used as is or is easily modified to support your application requirements. CustomToolBarButton ControlCustomToolBarButton Control OptionsUse the Notice how the New There are other simple methods to implement a ToolBar Button Requirements
In business applications, intrinsic security is a must. Security, like exception handling and logging, this is not something that you want to add in later. These are foundational considerations for business applications. Let me give you a security requirement example. You want to allow all system users to view the customer records, but only the order department and supervisors to edit customer records. This is very easily accomplished by altering which Custom Controls 101There are many resources here on Code Project, WPF books and Internet articles on authoring WPF Evaluate your requirements carefully. If a style will do, use a style instead of authoring a control. When authoring your control, attempt to design a control that derives its behavior and appearance from its own dependency properties. If you do this, consumers of your control won't have to write their own custom control template to get the results they need. Instead they can provide values to properties you have exposed. Consumers can set properties directly in the control XAML markup, in styles or code. Let's touch on the naming of controls inside your custom control templates. There is no need to name each control inside your custom control unless you will be accessing it in a trigger or in the control's code. If the control is only accessed in a trigger then you can use your standard control naming convention. If however the control is accessed in the control's code, you should follow the established naming convention of, " Use property triggers instead of code when possible. If you do this, it will make customizing your control by consumers much easier. To the degree that it is possible, your Our simple CustomToolBarButton Control ControlTemplate<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Core_WPF="clr-namespace:Core.WPF">
<Style TargetType="{x:Type Core_WPF:CustomToolBarButton}">
<Style.Resources>
<BooleanToVisibilityConverter x:Key="booleanToVisibilityConverter" />
<Core_WPF:CustomToolBarButtonImageSourceConverter
x:Key="customToolBarButtonImageSourceConverter" />
<!-- Disabled Brushes are used for the Disabled look of each control -->
<SolidColorBrush x:Key="DisabledForegroundBrush"
Color="#888" />
<SolidColorBrush x:Key="DisabledBackgroundBrush"
Color="#EEE" />
<SolidColorBrush x:Key="DisabledBorderBrush"
Color="#AAA" />
<!-- DefaultedBorderBrush is used to show KeyBoardFocus -->
<LinearGradientBrush x:Key="DefaultedBorderBrush"
EndPoint="0,1"
StartPoint="0,0">
<GradientStop Color="#777"
Offset="0.0" />
<GradientStop Color="#000"
Offset="1.0" />
</LinearGradientBrush>
</Style.Resources>
<Setter Property="BorderThickness"
Value=".7" />
<Setter Property="Padding"
Value="5" />
<Setter Property="IsTabStop"
Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Core_WPF:CustomToolBarButton}">
<Border x:Name="Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<StackPanel Orientation="{Binding Path=ButtonLayout,
RelativeSource={RelativeSource TemplatedParent}}">
<Image Stretch="None"
VerticalAlignment="Center"
Visibility="{Binding Path=ShowButtonImage,
RelativeSource={RelativeSource TemplatedParent},
Converter={StaticResource booleanToVisibilityConverter}}">
<Image.Source>
<MultiBinding Converter="{StaticResource
customToolBarButtonImageSourceConverter}">
<Binding Path="IsEnabled"
RelativeSource="{RelativeSource TemplatedParent}" />
<Binding Path="EnabledButtonImage"
RelativeSource="{RelativeSource TemplatedParent}" />
<Binding Path="DisabledButtonImage"
RelativeSource="{RelativeSource TemplatedParent}" />
</MultiBinding>
</Image.Source>
</Image>
<TextBlock x:Name="tbButtonText"
Text="{Binding Path=ButtonText,
RelativeSource={RelativeSource TemplatedParent}}"
Visibility="{Binding Path=ShowButtonText,
RelativeSource={RelativeSource TemplatedParent},
Converter={StaticResource
booleanToVisibilityConverter}}" />
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsKeyboardFocused"
Value="true">
<Setter Property="BorderBrush"
Value="{StaticResource DefaultedBorderBrush}"
TargetName="Border" />
</Trigger>
<Trigger Property="IsMouseOver"
Value="true">
<Setter Property="Foreground"
Value="{Binding Path=MouseOverForeground,
RelativeSource={RelativeSource TemplatedParent}}" />
<Setter Property="BorderBrush"
Value="{Binding Path=MouseOverBorder,
RelativeSource={RelativeSource TemplatedParent}}"
TargetName="Border" />
<Setter Property="BorderThickness"
Value="0.7"
TargetName="Border" />
<Setter Property="Background"
TargetName="Border"
Value="{Binding Path=MouseOverBackground,
RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
<Trigger Property="IsPressed"
Value="true">
<Setter Property="Background"
Value="{Binding Path=ButtonPressedBackground,
RelativeSource={RelativeSource TemplatedParent}}"
TargetName="Border" />
<Setter Property="BorderBrush"
Value="{Binding Path=ButtonPressedBorder,
RelativeSource={RelativeSource TemplatedParent}}"
TargetName="Border" />
<Setter Property="BorderThickness"
Value="0.7"
TargetName="Border" />
</Trigger>
<Trigger Property="IsEnabled"
Value="false">
<Setter Property="Foreground"
Value="{StaticResource DisabledForegroundBrush}" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="ShowButtonImage"
Value="True" />
<Condition Property="ShowButtonText"
Value="True" />
<Condition Property="ButtonLayout"
Value="Horizontal" />
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Margin"
TargetName="tbButtonText"
Value="5,0,0,0" />
</MultiTrigger.Setters>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
You will first notice that two converters have been added to the The We need our The The Are you seeing a pattern develop here? You define dependency properties in the control's code and then bind to them in the I better explain why I chose to have two images for each The The The last section of XAML is the If you were to write the above If Me.ShowButtonImage AndAlso Me.ShowButtonText AndAlso _
Me.ButtonLayout = Orientation.Horizontal Then
tbButtonText.Margin = New Thickness(5, 0, 0, 0)
End If
The other triggers are used to set the colors for the MultiValueConverterNamespace WPF
Public Class CustomToolBarButtonImageSourceConverter
Implements IMultiValueConverter
Public Function Convert(ByVal values() As Object, _
ByVal targetType As System.Type, ByVal parameter _
As Object, ByVal culture As _
System.Globalization.CultureInfo) As Object _
Implements _
System.Windows.Data.IMultiValueConverter.Convert
Dim strImage As String = String.Empty
If CType(values(0), Boolean) Then
strImage = values(1).ToString
Else
strImage = values(2).ToString
End If
If Not String.IsNullOrEmpty(strImage) Then
Dim objURI As New Uri(strImage, _
UriKind.Relative)
Return New BitmapImage(objURI)
Else
Return Nothing
End If
End Function
Public Function ConvertBack(ByVal value As Object, _
ByVal targetTypes() As System.Type, ByVal _
parameter As Object, ByVal culture As _
System.Globalization.CultureInfo) As Object() _
Implements _
System.Windows.Data.IMultiValueConverter.ConvertBack
Throw New System.NotImplementedException()
End Function
End Class
End Namespace
WPF In the above code, we simply casting the three elements in the values array, making a decision on which image to use and return either a CustomToolBarButton CodeImports System.ComponentModel
Namespace WPF
Public Class CustomToolBarButton
Inherits System.Windows.Controls.Button
#Region " Shared Declarations "
Public Shared ButtonLayoutProperty As DependencyProperty = _
DependencyProperty.Register("ButtonLayout", GetType(Orientation), _
GetType(CustomToolBarButton), New PropertyMetadata( _
Orientation.Horizontal))
Public Shared ButtonPressedBackgroundProperty As DependencyProperty = _
DependencyProperty.Register("ButtonPressedBackground", GetType(Brush), _
GetType(CustomToolBarButton))
Public Shared ButtonPressedBorderProperty As DependencyProperty = _
DependencyProperty.Register("ButtonPressedBorder", GetType(Brush), _
GetType(CustomToolBarButton))
Public Shared ButtonTextProperty As DependencyProperty = _
DependencyProperty.Register("ButtonText", GetType(String), GetType( _
CustomToolBarButton))
Public Shared DisabledButtonImageProperty As DependencyProperty = _
DependencyProperty.Register("DisabledButtonImage", GetType(String), _
GetType(CustomToolBarButton))
Public Shared EnabledButtonImageProperty As DependencyProperty = _
DependencyProperty.Register("EnabledButtonImage", GetType(String), _
GetType(CustomToolBarButton))
Public Shared MouseOverBackgroundProperty As DependencyProperty = _
DependencyProperty.Register("MouseOverBackground", GetType(Brush), _
GetType(CustomToolBarButton))
Public Shared MouseOverBorderProperty As DependencyProperty = _
DependencyProperty.Register("MouseOverBorder", GetType(Brush), GetType( _
CustomToolBarButton), New PropertyMetadata(New SolidColorBrush( _
Colors.Black)))
Public Shared MouseOverForegroundProperty As DependencyProperty = _
DependencyProperty.Register("MouseOverForeground", GetType(Brush), _
GetType(CustomToolBarButton))
Public Shared ShowButtonImageProperty As DependencyProperty = _
DependencyProperty.Register("ShowButtonImage", GetType(Boolean), _
GetType(CustomToolBarButton), New PropertyMetadata(True))
Public Shared ShowButtonTextProperty As DependencyProperty = _
DependencyProperty.Register("ShowButtonText", GetType(Boolean), _
GetType(CustomToolBarButton), New PropertyMetadata(False))
#End Region
#Region " Properties "
<Category("Custom"), Description( _
"This sets the position of the text in relation to the button image.")> _
Public Property ButtonLayout() As Orientation
Get
Return CType(GetValue(ButtonLayoutProperty), Orientation)
End Get
Set(ByVal value As Orientation)
SetValue(ButtonLayoutProperty, value)
End Set
End Property
<Category("Custom"), Description("Button pressed background brush.")> _
Public Property ButtonPressedBackground() As Brush
Get
Return CType(GetValue(ButtonPressedBackgroundProperty), Brush)
End Get
Set(ByVal value As Brush)
SetValue(ButtonPressedBackgroundProperty, value)
End Set
End Property
<Category("Custom"), Description("Button pressed border brush.")> _
Public Property ButtonPressedBorder() As Brush
Get
Return CType(GetValue(ButtonPressedBorderProperty), Brush)
End Get
Set(ByVal value As Brush)
SetValue(ButtonPressedBorderProperty, value)
End Set
End Property
<Category("Custom"), Description("Text for the button.")> _
Public Property ButtonText() As String
Get
Return CType(GetValue(ButtonTextProperty), String)
End Get
Set(ByVal value As String)
SetValue(ButtonTextProperty, value)
End Set
End Property
''' <summary>
''' I did this since we have derived from button, but don't use the
''' content property like other buttons. The control template for
''' this control is the content of this button. Doing this, just
''' prevents this property from showing up in the GUI designers.
''' </summary>
<System.ComponentModel.Browsable(False)> _
Public Shadows Property Content() As Object
Get
Return MyBase.Content
End Get
Set(ByVal value As Object)
MyBase.Content = value
End Set
End Property
<Category("Custom"), Description( _
"Image to display when the button is disabled.")> _
Public Property DisabledButtonImage() As String
Get
Return CType(GetValue(DisabledButtonImageProperty), String)
End Get
Set(ByVal value As String)
SetValue(DisabledButtonImageProperty, value)
End Set
End Property
<Category("Custom"), Description( _
"Image to display when the button is enabled.")> _
Public Property EnabledButtonImage() As String
Get
Return CType(GetValue(EnabledButtonImageProperty), String)
End Get
Set(ByVal value As String)
SetValue(EnabledButtonImageProperty, value)
End Set
End Property
<Category("Custom"), Description("Mouse over background brush.")> _
Public Property MouseOverBackground() As Brush
Get
Return CType(GetValue(MouseOverBackgroundProperty), Brush)
End Get
Set(ByVal value As Brush)
SetValue(MouseOverBackgroundProperty, value)
End Set
End Property
<Category("Custom"), Description("Mouse over border brush.")> _
Public Property MouseOverBorder() As Brush
Get
Return CType(GetValue(MouseOverBorderProperty), Brush)
End Get
Set(ByVal value As Brush)
SetValue(MouseOverBorderProperty, value)
End Set
End Property
<Category("Custom"), Description("Mouse over foreground text brush.")> _
Public Property MouseOverForeground() As Brush
Get
Return CType(GetValue(MouseOverForegroundProperty), Brush)
End Get
Set(ByVal value As Brush)
SetValue(MouseOverForegroundProperty, value)
End Set
End Property
<Category("Custom"), Description("Display the image on the button.")> _
Public Property ShowButtonImage() As Boolean
Get
Return CType(GetValue(ShowButtonImageProperty), Boolean)
End Get
Set(ByVal value As Boolean)
SetValue(ShowButtonImageProperty, value)
End Set
End Property
<Category("Custom"), Description("Display the text on the button.")> _
Public Property ShowButtonText() As Boolean
Get
Return CType(GetValue(ShowButtonTextProperty), Boolean)
End Get
Set(ByVal value As Boolean)
SetValue(ShowButtonTextProperty, value)
End Set
End Property
#End Region
#Region " Constructor "
Shared Sub New()
DefaultStyleKeyProperty.OverrideMetadata(GetType(CustomToolBarButton), _
New FrameworkPropertyMetadata(GetType(CustomToolBarButton)))
End Sub
#End Region
End Class
| ||||||||||||||||||||