WPF - The value specified in System.Windows.Markup.StaticResourceHolder caused an exception - wpf

So Once again i came here to get some anwsers to problems i can't resolve them by myself, so my problem is im getting a xamlparse execption each time the button style is loaded, inside the style i have a < rectangle > that should get one color defined by the user from a query on my MVVM. So far there was no problems, the problem is trying to give that color value to the grandientstop inside the LinearGradientBrush of the rectangle. The xaml code im using:
<Rectangle x:Name="rectangle" Fill="{Binding Path=StrColor, Converter={StaticResource FadingBrushConverter}, RelativeSource={RelativeSource AncestorType={x:Type Rectangle}}}" HorizontalAlignment="Right" Height="70" Margin="0,0,0,0" RadiusY="5" RadiusX="5" VerticalAlignment="Bottom" Width="70">
<Rectangle.Effect>
<DropShadowEffect ShadowDepth="1" BlurRadius="8"/>
</Rectangle.Effect>
</Rectangle>
The "StrColor" is a Color property.
On my MVVM i have this converter:
Public Class FadingBrushConverter
Implements IValueConverter
Public Function Convert(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
' TODO: Do some type-checking
Dim brush = New LinearGradientBrush()
Dim color = DirectCast(value, Color)
Dim Bcolor As System.Windows.Media.Color
Bcolor.R = 0
Bcolor.G = 0
Bcolor.B = 0
brush.StartPoint = New Point(0.5, 0)
brush.EndPoint = New Point(0.5, 1.27)
brush.GradientStops.Add(New GradientStop(color, 0))
brush.GradientStops.Add(New GradientStop(Bcolor, 1))
Return brush
End Function
Public Function ConvertBack(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
Throw New NotSupportedException()
End Function
End Class
I really dont know what am i doing wrong here, i have been looking on the internet but so far i had no luck in fixing this problem!
I also have used DynamicResource but with no success!
Thanks for any help!

I think the problem is with the binding. "Fill" property of the Rectangle is bound to the strColor property of its parent Rectangle (with the "RelativeSource FindAncestor" argument). I don't think it is realistic to assume that a rectangle is placed in another rectangle. Even if is is somehow the case, the built-in Rectangle element doesn't have a strColor property. If you want to get the color from the ViewModel, you should try something in the sort of:
{Binding Path=StrColor, Converter={StaticResource FadingBrushConverter}"
Of course, this is assuming that the DataContext is set correctly.

Related

MultiBinding Shows Build Error. Is this a fault in xaml or the converter

From within a custom Control.
Having tried , without much success to get PriorityBinding to work I've decided to look at MultiBinding instead.
In xaml the binding is set like this;
<StackPanel>
<Image Style="{StaticResource vtlImageStyle}">
<Image.Source>
<MultiBinding Converter="{StaticResource MultivalueConverter}">
<Binding Path="FirstImage"
RelativeSource="{RelativeSource TemplatedParent}" />
<Binding Path="DefaultFirstButtonImage"
RelativeSource="{RelativeSource TemplatedParent}" />
</MultiBinding>
</Image.Source>
</Image>
</StackPanel>
and the converter being used is defined like this:
Public Class VtlDefaultToChosenImageConverter
Implements IMultiValueConverter
'Expects images to be passed in as user chosen followed by default so that user chosen will always triumph over default.'
Public Function Convert(values() As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IMultiValueConverter.Convert
Dim img As ImageSource = Nothing
Dim img1 As ImageSource = CType(values(0), ImageSource)
Dim img2 As ImageSource = CType(values(1), ImageSource)
If Not IsNothing(img1) Then
img = img1
Else
If Not IsNothing(img2) Then
img = img2
End If
End If
Return img
End Function
Public Function ConvertBack(value As Object, targetTypes() As Type, parameter As Object, culture As CultureInfo) As Object() Implements IMultiValueConverter.ConvertBack
Throw New NotImplementedException()
End Function
End Class
The idea is that in the event of there being no FirstImage set by the consumer of the control it should fall back on the default image. In this basic guise this works.
Now if I (when consuming the control) add my own choice of FirstImage the ide shows a build error in the ide, and the new FirstImage property is underlined in xaml. When one actually runs the application the control will compile and show the new FirstImage. Should I expect to see this sort of error, or is there a mistake in the way that I have either definded the binding in xaml or the actual converter?

Wpf UserControl with its own data context and external dependency property

I'm trying to create a simple AudioPlayer control multiple reuse in a solution I'm working on. I have seen numerous example in various posts and blogs around the net and from those have created a small control with four buttons.
The xaml is defined thus:
<UserControl x:Class="AudioPlayer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="150">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Margin" Value="10,0,0,0" />
</Style>
</StackPanel.Resources>
<MediaElement Name="media" Source="{Binding Source}" LoadedBehavior="{Binding LoadedBehavior}"/>
<Button Width="24" Height="24" x:Name="Repeat" Background="Transparent" BorderBrush="Transparent">
<Image Source="Images/button_blue_repeat.png" ToolTip="Repeat"/>
</Button>
<Button Width="24" Height="24" x:Name="Play" Background="Transparent" BorderBrush="Transparent">
<Image Source="Images/button_blue_play.png" ToolTip="Play"/>
</Button>
<Button Width="24" Height="24" x:Name="Pause" Background="Transparent" BorderBrush="Transparent">
<Image Source="Images/button_blue_pause.png" ToolTip="Pause"/>
</Button>
<Button Width="24" Height="24" x:Name="Stop" Background="Transparent" BorderBrush="Transparent">
<Image Source="Images/button_blue_stop.png" ToolTip="Stop"/>
</Button>
</StackPanel>
With fairly simple code in the background;
Public Class AudioPlayer
Public Sub New()
InitializeComponent()
DataContext = New AudioPlayerViewModel With {.MediaElement = media, .Source = "bag1.mp3", .LoadedBehavior = MediaState.Manual, .CanCommandExecute = True}
End Sub
End Class
Public Class AudioPlayerViewModel
Inherits DependencyObject
Public Sub New()
Me.MediaCommand = New MediaElementCommand(Me)
End Sub
Public Property MediaElement() As MediaElement
Public Property Source() As String
Public Property LoadedBehavior() As MediaState
Public Property CanCommandExecute() As Boolean
Public Property MediaCommand() As ICommand
End Class
Public Class MediaElementCommand
Implements ICommand
Private vm As AudioPlayerViewModel
Public Sub New(ByVal vm As AudioPlayerViewModel)
Me.vm = vm
End Sub
Public Function CanExecute(ByVal parameter As Object) As Boolean Implements ICommand.CanExecute
Return vm.CanCommandExecute
End Function
Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
AddHandler(ByVal value As EventHandler)
AddHandler CommandManager.RequerySuggested, value
End AddHandler
RemoveHandler(ByVal value As EventHandler)
RemoveHandler CommandManager.RequerySuggested, value
End RemoveHandler
RaiseEvent(ByVal sender As System.Object, ByVal e As System.EventArgs)
End RaiseEvent
End Event
Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
Dim action As String = DirectCast(parameter, String)
Select Case action.ToLower()
Case "play"
vm.MediaElement.Position = TimeSpan.Zero
vm.MediaElement.Play()
Case "stop"
vm.MediaElement.Stop()
Case "pause"
vm.MediaElement.Pause()
Case "resume"
vm.MediaElement.Play()
Case Else
Throw New NotSupportedException(String.Format("Unknown media action {0}", action))
End Select
End Sub
End Class
My question quite simply is this. From the code you can see that at present the sound that is being played is hard coded. What I would like to know is wheteher it would be possible to create a dependency property for this control (I presume it would be of type string to represent a path to a sound file but I'm not sure) so that when the control is created in other controls or windows their viewmodels can pass a sound property to it (if that makes sense!).
If it is possible where should I create it in respect of the code snippets shown?
Many thanks
You could create a DP, but it would not work the way users would expect.
For example, if the user were to write
<local:AudioPlayer Media="{Binding SomeString}" />
Then WPF tries to set Media = DataContext.SomeString
But since you have hardcoded DataContext = New AudioPlayerViewModel in the constructor, then the binding will most likely fail because users will be expecting their inherited DataContext to be used by the UserControl, but the hardcoded DataContext will be used instead.
It is always my advice to never hardcode the DataContext property inside of a UserControl. It breaks the entire WPF design pattern of having separate layers for UI and Data.
Either build a UserControl specifically for use with a specific Model or ViewModel being used as the DataContext, such as this :
<!-- Draw anything of type AudioPlayerViewModel with control AudioPlayer -->
<!-- DataContext will automatically set to the AudioPlayerViewModel -->
<DataTemplate DataType="{x:Type local:AudioPlayerViewModel}}">
<local:AudioPlayer />
</DataTemplate>
Or build it with the expectation that the DataContext can be absolutely anything, and DependencyProperites will be used to give the control the data it needs :
<!-- DataContext property can be anything, as long as it as the property MyString -->
<local:AudioPlayer Media="{Binding MyString}" />
The easiest way to get your code to work would probably be
Create the ViewModel as a private property instead of assiging it to the UserControl.DataContext
Bind or set the DataContext of the top level child inside your UserControl to your private property (in your case, the StackPanel)
Adjust the binding for your MediaElement to read from a custom DependencyProperty instead of from StackPanel.DataContext
Something like this :
<UserControl x:Name="MyAudioPlayer" ...>
<StackPanel x:Name="AudioPlayerRoot">
...
<MediaElement Source="{Binding ElementName=MyAudioPlayer, Path=MediaDependecyProperty}" ... />
...
</StackPanel>
</UserControl>
Public Sub New()
InitializeComponent()
AudioPlayerRoot.DataContext = New AudioPlayerViewModel ...
End Sub

WPF: Placing a textbox randomly on a canvas

I have a canvas of sorts derived from a 'Panel' used for custom drawing of lines and other geometries, all from VB code. I got this approach from a book and I'm not sure it's the best approach. The drawing part works so far for me.
But what I need is to put a textbox control on the control containing text which can be edited by the user. The textbox needs to be placed at coordinates determined dynamically and later deleted. There will probably be other controls handled so.
The following code does nothing:
tb = New TextBox()
tb.Text = "How now brown cow?"
tb.BorderThickness = New Thickness(3)
tb.BorderBrush = Brushes.CadetBlue
drawingSurface.Children.Add(tb)
This is the definition of my DrawingCanvas:
Public Class DrawingCanvas
Inherits Panel
Private visuals As New List(Of Visual)()
Private hits As New List(Of DrawingVisual)()
Protected Overrides Function GetVisualChild(ByVal index As Integer) As Visual
Return visuals(index)
End Function
Protected Overrides ReadOnly Property VisualChildrenCount() As Integer
Get
Return visuals.Count
End Get
End Property
Public Sub AddVisual(ByVal visual As Visual)
visuals.Add(visual)
MyBase.AddVisualChild(visual)
MyBase.AddLogicalChild(visual)
End Sub
Public Sub DeleteVisual(ByVal visual As Visual)
visuals.Remove(visual)
MyBase.RemoveVisualChild(visual)
MyBase.RemoveLogicalChild(visual)
End Sub
Public Function GetVisual(ByVal point As Point) As DrawingVisual
Dim hitResult As HitTestResult = VisualTreeHelper.HitTest(Me, point)
Return TryCast(hitResult.VisualHit, DrawingVisual)
End Function
Public Function GetVisuals(ByVal region As Geometry) As List(Of DrawingVisual)
hits.Clear()
Dim parameters As New GeometryHitTestParameters(region)
Dim callback As New HitTestResultCallback(AddressOf Me.HitTestCallback)
VisualTreeHelper.HitTest(Me, Nothing, callback, parameters)
Return hits
End Function
Private Function HitTestCallback(ByVal result As HitTestResult) As HitTestResultBehavior
Dim geometryResult As GeometryHitTestResult = CType(result, GeometryHitTestResult)
Dim visual As DrawingVisual = TryCast(result.VisualHit, DrawingVisual)
If visual IsNot Nothing AndAlso geometryResult.IntersectionDetail = IntersectionDetail.FullyInside Then
hits.Add(visual)
MsgBox("Ouch")
End If
Return HitTestResultBehavior.Continue
End Function
End Class
Here is the XAML. I added a textbox to the DrawingCanvas just to see if something appears. Nothing did. In fact, I want to do this in code, not XAML. I thought I could hide or move it around dynamically.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Music"
Title="MainWindow" Height="539" Width="892">
<DockPanel>
<Menu DockPanel.Dock="Top" Name="MainMenu" VerticalAlignment="Top" Height="25">
<MenuItem Name="File" Header="File">
<MenuItem Name="Open" Header="Bla bla..."/>
</MenuItem>
</Menu>
<local:DrawingCanvas DockPanel.Dock="Bottom" x:Name="drawingSurface" RenderTransformOrigin="0.5,0.5" >
<TextBox Height="0" Name="TextBox1" Width="45" Text="How now brown cow?" />
</local:DrawingCanvas>
</DockPanel>
</Window>
Thanks for helping a nooby. A solution would be very useful for me. This was easy with windows forms, but I need the drawing speed of WPF.
I think you are a bit off here. In WPF you have a control called Canvas. I would suggest you use that instead of your own "DrawingCanvas", which I can't get to work btw. :( (For some reason I cant create code blocks so if someone can edit it I would be pleased)
Anyhow,
<local:DrawingCanvas DockPanel.Dock="Bottom" x:Name="drawingSurface" RenderTransformOrigin="0.5,0.5" >
<TextBox Height="0" Name="TextBox1" Width="45" Text="How now brown cow?" />
</local:DrawingCanvas>
Turns into:
<Canvas x:Name="drawingSurface">
</Canvas>
And then to add a textbox just do as your current code:
Dim tb as New TextBox
drawingSurface.Children.Add(tb)
This should give you what you need.
Heres the code for adding a rectangle to your canvas.
Private Sub DrawBackground()
Dim Rect As New Rectangle()
Rect.Height = 50
Rect.Width = 50
Rect.Fill = Brushes.Cornsilk
drawingSurface.SetTop(Rect, 30)
drawingSurface.SetLeft(Rect, 100)
drawingSurface.Children.Add(Rect)
End Sub
I'll add another answer that might be more in the line of what you are looking for. This is a class that inherits from Canvas that will allow you to draw stuff in the same way as say you do in your comment.
I also creates a textbox on a random location when it is created.
Public Class DrawingCanvas
Inherits Canvas
Public RandomTextBox As New TextBox
Protected Overrides Sub OnRender(dc As System.Windows.Media.DrawingContext)
Dim brush As Brush = Brushes.Black
Dim drawingPen As Pen = New Pen(Brushes.Green, 3)
dc.DrawRectangle(brush, drawingPen, New Rect(5, 5, Me.ActualWidth - 5, Me.ActualHeight - 5))
RandomTextBox.Text = "Herpdiderp"
If Not Me.Children.Contains(RandomTextBox) Then
Dim r As New Random()
RandomTextBox.Height = 23
RandomTextBox.Width = 100
Me.SetTop(RandomTextBox, r.Next(0, Me.ActualHeight - RandomTextBox.Height))
Me.SetLeft(RandomTextBox, r.Next(0, Me.ActualWidth - RandomTextBox.Width))
Me.Children.Add(RandomTextBox)
End If
End Sub
End Class
This is not a full answer. #WozzeC, you were right about using the canvas - almost.
I have managed to solve this in xaml alone - I want to eventually solve it in vb.net.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DockPanel HorizontalAlignment="Stretch" Name="DockPanel1" VerticalAlignment="Stretch" >
<Menu Height="23" DockPanel.Dock="Top" Name="Menu1" VerticalAlignment="Top" />
<Canvas Name="Canvas1" Background="Aquamarine">
<TextBox Canvas.Left="118" HorizontalScrollBarVisibility="Disabled" Canvas.Top="81" AcceptsReturn="True" Height="auto" Name="TextBox1" Width="68" Text="Herpdiderp" BorderThickness="0" Background="Aquamarine" />
</Canvas>
</DockPanel>
</Grid>
And here is a piece of code that expands the text as needed. I think it's almost totally cool. It expands both to the right and downwards, as if you're actually typing on the form. It adds a little too much on the right, but it's not visible in this version because the background color is the same.
Here is the event code that expands it to the right.
Imports System.Globalization
Class MainWindow
Private Sub TextBox1_TextChanged(sender As System.Object, e As System.Windows.Controls.TextChangedEventArgs) Handles TextBox1.TextChanged
Dim ft As New FormattedText(TextBox1.Text, CultureInfo.GetCultureInfo("en-us"), FlowDirection.LeftToRight, New Typeface("Verdana"), 16, Brushes.Black)
TextBox1.Width = ft.Width
End Sub
End Class
I tried this with my existing solution and the textbox does not appear. I made the DrawingCanvas into a plain Canvas and commented out all the code referring to the DrawingCanvas. And the textbox does appear. The problem is this: I need the functionality in the DrawingCanvas - which derives from Canvas. But because the baseclass methods are Protected, I can't get to them. I can only use them in a derived class, unless there is another way I don't know about.
Any ideas about how to solve this?

WPF Hide DataGridColumn via a binding

For some reason I can't hide WPF Toolkit's DataGridColumn. I am trying to do the following:
<dg:DataGridTemplateColumn Header="Item Description" Visibility="{Binding IsReadOnly}">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=ItemDescription}" />
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
This doesn't work, since it's looking for a IsReadOnly property on the ItemSource (not a property of the current class).
If add this as a property of the ItemSource class that implements INoifyPropertyChanged, it still doesn't hide the column. Is there a way around this? I want the column to hid when a button click changes IsReadOnly property.
Assume IsReadOnly returns a Visibility value and is a dependency property
I am completely stuck, I would really appreciate the help! Thanks a lot!
Posted in this question:
WPF DataGrid: Binding DataGridColumn visibility to ContextMenu MenuItems Ischeked (MVVM)
Fubzot is using the binding code similar to
Visibility='{Binding (FrameworkElement.DataContext).IsReadOnly,
RelativeSource={x:Static RelativeSource.Self}}'
You may also want to check out this:
Forwarding the DataGrid’s DataContext to its’ columns..
which is also linked in the above question.
Just for my information: Do you see any Binding errors in your Output window using your current code?
If you want to bind to the DataGridColumn's IsReadOnly property, just add a RelativeSource to the Binding (and a converter):
<BooleanToVisibilityConverter x:Key="boolToVis" />
...
<dg:DataGridTemplateColumn Header="Item Description" Visibility="{Binding IsReadOnly, RelativeSource={RelativeSource Self}, Converter={StaticResource boolToVis}}">
Also, it looks like this StackOverflow question might be related to your problem.
you need to use a converter
Public Class BooleanToVisibilityConverter
Implements IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
If targetType Is GetType(Visibility) Then
If CBool(value) = True Then
Return Visibility.Hidden
Else
Return Visibility.Visible
End If
Else
Return Nothing
End If
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Return Nothing
End Function
End Class
then you use the converter in the XAML. SAMPLE

How do I create a WPF Rounded Corner container?

We are creating an XBAP application that we need to have rounded corners in various locations in a single page and we would like to have a WPF Rounded Corner container to place a bunch of other elements within. Does anyone have some suggestions or sample code on how we can best accomplish this? Either with styles on a or with creating a custom control?
You don't need a custom control, just put your container in a border element:
<Border BorderBrush="#FF000000" BorderThickness="1" CornerRadius="8">
<Grid/>
</Border>
You can replace the <Grid/> with any of the layout containers...
I know that this isn't an answer to the initial question ... but you often want to clip the inner content of that rounded corner border you just created.
Chris Cavanagh has come up with an excellent way to do just this.
I have tried a couple different approaches to this ... and I think this one rocks.
Here is the xaml below:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="Black"
>
<!-- Rounded yellow border -->
<Border
HorizontalAlignment="Center"
VerticalAlignment="Center"
BorderBrush="Yellow"
BorderThickness="3"
CornerRadius="10"
Padding="2"
>
<Grid>
<!-- Rounded mask (stretches to fill Grid) -->
<Border
Name="mask"
Background="White"
CornerRadius="7"
/>
<!-- Main content container -->
<StackPanel>
<!-- Use a VisualBrush of 'mask' as the opacity mask -->
<StackPanel.OpacityMask>
<VisualBrush Visual="{Binding ElementName=mask}"/>
</StackPanel.OpacityMask>
<!-- Any content -->
<Image Source="http://chriscavanagh.files.wordpress.com/2006/12/chriss-blog-banner.jpg"/>
<Rectangle
Height="50"
Fill="Red"/>
<Rectangle
Height="50"
Fill="White"/>
<Rectangle
Height="50"
Fill="Blue"/>
</StackPanel>
</Grid>
</Border>
</Page>
I just had to do this myself, so I thought I would post another answer here.
Here is another way to create a rounded corner border and clip its inner content. This is the straightforward way by using the Clip property. It's nice if you want to avoid a VisualBrush.
The xaml:
<Border
Width="200"
Height="25"
CornerRadius="11"
Background="#FF919194"
>
<Border.Clip>
<RectangleGeometry
RadiusX="{Binding CornerRadius.TopLeft, RelativeSource={RelativeSource AncestorType={x:Type Border}}}"
RadiusY="{Binding RadiusX, RelativeSource={RelativeSource Self}}"
>
<RectangleGeometry.Rect>
<MultiBinding
Converter="{StaticResource widthAndHeightToRectConverter}"
>
<Binding
Path="ActualWidth"
RelativeSource="{RelativeSource AncestorType={x:Type Border}}"
/>
<Binding
Path="ActualHeight"
RelativeSource="{RelativeSource AncestorType={x:Type Border}}"
/>
</MultiBinding>
</RectangleGeometry.Rect>
</RectangleGeometry>
</Border.Clip>
<Rectangle
Width="100"
Height="100"
Fill="Blue"
HorizontalAlignment="Left"
VerticalAlignment="Center"
/>
</Border>
The code for the converter:
public class WidthAndHeightToRectConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double width = (double)values[0];
double height = (double)values[1];
return new Rect(0, 0, width, height);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
VB.Net code based implementation of kobusb's Border control solution. I used it to populate a ListBox of Button controls. The Button controls are created from MEF extensions. Each extension uses MEF's ExportMetaData attribute for a Description of the extension. The extensions are VisiFire charting objects. The user pushes a button, selected from the list of buttons, to execute the desired chart.
' Create a ListBox of Buttons, one button for each MEF charting component.
For Each c As Lazy(Of ICharts, IDictionary(Of String, Object)) In ext.ChartDescriptions
Dim brdr As New Border
brdr.BorderBrush = Brushes.Black
brdr.BorderThickness = New Thickness(2, 2, 2, 2)
brdr.CornerRadius = New CornerRadius(8, 8, 8, 8)
Dim btn As New Button
AddHandler btn.Click, AddressOf GenericButtonClick
brdr.Child = btn
brdr.Background = btn.Background
btn.Margin = brdr.BorderThickness
btn.Width = ChartsLBx.ActualWidth - 22
btn.BorderThickness = New Thickness(0, 0, 0, 0)
btn.Height = 22
btn.Content = c.Metadata("Description")
btn.Tag = c
btn.ToolTip = "Push button to see " & c.Metadata("Description").ToString & " chart"
Dim lbi As New ListBoxItem
lbi.Content = brdr
ChartsLBx.Items.Add(lbi)
Next
Public Event Click As RoutedEventHandler
Private Sub GenericButtonClick(sender As Object, e As RoutedEventArgs)
Dim btn As Button = DirectCast(sender, Button)
Dim c As Lazy(Of ICharts, IDictionary(Of String, Object)) = DirectCast(btn.Tag, Lazy(Of ICharts, IDictionary(Of String, Object)))
Dim w As Window = DirectCast(c.Value, Window)
Dim cc As ICharts = DirectCast(c.Value, ICharts)
c.Value.CreateChart()
w.Show()
End Sub
<System.ComponentModel.Composition.Export(GetType(ICharts))> _
<System.ComponentModel.Composition.ExportMetadata("Description", "Data vs. Time")> _
Public Class DataTimeChart
Implements ICharts
Public Sub CreateChart() Implements ICharts.CreateChart
End Sub
End Class
Public Interface ICharts
Sub CreateChart()
End Interface
Public Class Extensibility
Public Sub New()
Dim catalog As New AggregateCatalog()
catalog.Catalogs.Add(New AssemblyCatalog(GetType(Extensibility).Assembly))
'Create the CompositionContainer with the parts in the catalog
ChartContainer = New CompositionContainer(catalog)
Try
ChartContainer.ComposeParts(Me)
Catch ex As Exception
Console.WriteLine(ex.ToString)
End Try
End Sub
' must use Lazy otherwise instantiation of Window will hold open app. Otherwise must specify Shutdown Mode of "Shutdown on Main Window".
<ImportMany()> _
Public Property ChartDescriptions As IEnumerable(Of Lazy(Of ICharts, IDictionary(Of String, Object)))
End Class
If you're trying to put a button in a rounded-rectangle border, you should check out msdn's example. I found this by googling for images of the problem (instead of text). Their bulky outer rectangle is (thankfully) easy to remove.
Note that you will have to redefine the button's behavior (since you've changed the ControlTemplate). That is, you will need to define the button's behavior when clicked using a Trigger tag (Property="IsPressed" Value="true") in the ControlTemplate.Triggers tag. Hope this saves someone else the time I lost :)

Resources