How to take a screenshot of the content of a ScrollViewer - wpf

I'd like to take a screenshot of whatever is inside a ScrollViewer, not just what is seen inside the ViewPort. But everything I tried so far results in completely black images or in images where only parts of the content are shown and everything else is black. I assumed that it's maybe because I didn't specify any backgrounds, but changing the background of my Window or my TreeView to White didn't change a thing.
Can you tell me what is wrong or how else I can take a screenshot of the full content of a ScrollViewer?
Here's a little sample I put together (press 'P' after the main window is shown).
The main window (just a TreeView in a Window that is too small to show the full content):
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ScreenshotTest"
mc:Ignorable="d"
Title="MainWindow"
Height="200" Width="300"
KeyDown="Window_KeyDown">
<TreeView x:Name="Tree">
<TreeViewItem Header="Node 1 - Lorem ipsum dolor sit amet, consetetur sadipscing elitr" IsSelected="True" IsExpanded="True">
<TreeViewItem Header="Node 1.1" />
<TreeViewItem Header="Node 1.2" IsExpanded="True">
<TreeViewItem Header="Node 1.2.1"/>
</TreeViewItem>
<TreeViewItem Header="Node 1.3" IsExpanded="True">
<TreeViewItem Header="Node 1.3.1" />
<TreeViewItem Header="Node 1.3.2" />
<TreeViewItem Header="Node 1.3.3" />
</TreeViewItem>
</TreeViewItem>
<TreeViewItem Header="Node 2 - sed diam nonumy eirmod tempor invidunt ut labore et dolore" IsExpanded="True">
<TreeViewItem Header="Node 2.1"/>
<TreeViewItem Header="Node 2.2" IsExpanded="True">
<TreeViewItem Header="Node 2.2.1"/>
</TreeViewItem>
<TreeViewItem Header="Node 2.3" IsExpanded="True">
<TreeViewItem Header="Node 2.3.1" />
<TreeViewItem Header="Node 2.3.2" />
<TreeViewItem Header="Node 2.3.3" />
</TreeViewItem>
</TreeViewItem>
</TreeView>
</Window>
And the code behind to the window:
Class MainWindow
Private Sub Window_KeyDown(sender As Object, e As KeyEventArgs)
If e.Key <> Key.P Then
Exit Sub
End If
Dim sv As ScrollViewer = GetFirstChildOfType(Of ScrollViewer)(Me.Tree)
Me.CaptureScreen(sv.Content, "C:\Temp\Screenshot.png")
End Sub
Private Shared Function GetFirstChildOfType(Of T As DependencyObject)(obj As DependencyObject) As T
Dim result As T
Dim child As DependencyObject
If (obj Is Nothing) Then
Return Nothing
End If
If (VisualTreeHelper.GetChildrenCount(obj) = 0) Then
Return Nothing
End If
For index As Integer = 0 To VisualTreeHelper.GetChildrenCount(obj)
child = VisualTreeHelper.GetChild(obj, index)
result = TryCast(child, T)
If (result Is Nothing) Then
result = GetFirstChildOfType(Of T)(child)
End If
If (result IsNot Nothing) Then
Return result
End If
Next index
Return Nothing
End Function
Private Sub CaptureScreen(source As UIElement, filename As String)
Dim height As Double
Dim renderHeight As Double
Dim width As Double
Dim renderWidth As Double
Dim renderTarget As RenderTargetBitmap
Dim vb As VisualBrush
Dim dv As DrawingVisual
Dim encoder As PngBitmapEncoder
Try
height = source.RenderSize.Height
renderHeight = height
width = source.RenderSize.Width
renderWidth = width
renderTarget = New RenderTargetBitmap(Convert.ToInt32(renderWidth), Convert.ToInt32(renderHeight), 96, 96, PixelFormats.Pbgra32)
vb = New VisualBrush(source)
dv = New DrawingVisual
Using dc As DrawingContext = dv.RenderOpen
dc.DrawRectangle(vb, Nothing, New Rect(New Point(0, 0), New Point(width, height)))
End Using
renderTarget.Render(dv)
encoder = New PngBitmapEncoder
encoder.Frames.Add(BitmapFrame.Create(renderTarget))
Using fs As IO.FileStream = New IO.FileStream(filename, IO.FileMode.Create, IO.FileAccess.Write)
encoder.Save(fs)
End Using
Catch ex As Exception
Stop
End Try
End Sub
End Class
I'm fine with answers in C# as well.

After a lot of trial and error I found out, that I got several things wrong.
First, I used the wrong UIElement. I had to take the first StackPanel instead of the first ScrollViewer.
Second, although my Window has the background set to white, all my controls inside Window doesn't have a background, that's probably why my resulting picture is black.
So, changing just one function of my code to
Private Sub Window_KeyDown(sender As Object, e As KeyEventArgs)
Dim panel As StackPanel
If e.Key <> Key.P Then
Exit Sub
End If
panel = GetFirstChildOfType(Of StackPanel)(Me.Tree)
panel.Background = Brushes.White
panel.UpdateLayout()
Me.CaptureScreen(panel, "C:\Temp\Screenshot.png")
End Sub
did the trick, now I at least get a picture with a white background and all the elements of my TreeView, not just the ones visible on screen.
Now I have some other problems, but that's an issue for another question.

Related

How do I find the image in a WPF Datagrid ColumnHeader so I can change the image?

I'm attempting to implement Excel-like column filtering and sorting. To do this, I used a DataTemplate to define the Column Header.
<DataGrid x:Name="dataGrid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CanUserSortColumns="False">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="23"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="ExcelFilterButton" Tag="{Binding}" Click="ExcelFilterButton_Click" Margin="0,0,0,0" BorderThickness="0" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" Focusable="False" Grid.Column="0">
<Image Source="Resources\NoSortNoFilter.png" Width="19" Height="19" />
</Button>
<TextBlock Text="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="1" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
</DataGrid>
And it comes out nicely.
I tried using VisualTreeHelper to find the image from the Column Header, but the Header property is a string. I've tried using the HeaderStyle and HeaderTemplate properties also but to no avail.
Using a WPF Spy program called Snoop, I can see the image in there, but still can't figure out how to access it in code. The reason I need to access it in code is to change the image based on whether that column is sorted and/or filtered. (Could this be done in XAML?)
Ok, I figured out how to do it. This most likely not the right way to do it, but I found a way that works.
To give you a little about the process.
The user clicks a header button. The buttons Tag property is bound to the column header.
The click event handler instantiates the context menu and sets its Tag to equal the button Tag.
The user clicks on a menu item.
The event handler sends the Context Menu Tag property and image name to the routine that finds the button, and then the image in the button, and changes the image.
now for the code.
The button click event handler:
Private Sub ExcelFilterButton_Click(sender As Object, e As RoutedEventArgs)
With DirectCast(Resources("sortContextMenu"), ContextMenu)
.Tag = DirectCast(sender, Button).Tag
.IsOpen = True
End With
End Sub
The menu item click event handler
Private Sub ContextMenuItem_Click(Sender As Object, e As RoutedEventArgs)
If TypeOf Sender Is MenuItem Then
'just testing, of course this isn't all this handler does.
SetColumnSortImage(Sender.Tag, "Filtered")
End If
End Sub
The SetColumnSortImage routine, which calls the two following routines.
Private Sub SetColumnSortImage(Tag As String, ImageName As String)
Dim btn As Button = Nothing
GetSortButton(Of Button)(dataGrid, Tag, btn)
If btn IsNot Nothing Then
Dim img As Image = GetChildOfType(Of Image)(btn)
img.Source = New BitmapImage(New Uri("pack://application:,,,/Resources/" & ImageName & ".png"))
End If
End Sub
The GetSortButton routine
Private Sub GetSortButton(Of T As DependencyObject)(dep As DependencyObject, Tag As String, ByRef out As DependencyObject)
If dep IsNot Nothing Then
If TypeOf dep Is Button AndAlso CType(dep, Button).Tag = Tag Then
out = dep
Else
If VisualTreeHelper.GetChildrenCount(dep) > 0 Then
For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(dep) - 1
GetSortButton(Of T)(VisualTreeHelper.GetChild(dep, i), Tag, out)
Next
End If
End If
End If
End Sub
This routine was found elsewhere on StackOverflow in C#. I converted it to VB.
Private Function GetChildOfType(Of T As DependencyObject)(depObj As DependencyObject) As T
If depObj Is Nothing Then
Return Nothing
End If
For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(depObj) - 1
Dim child = VisualTreeHelper.GetChild(depObj, i)
Dim result = If(TryCast(child, T), GetChildOfType(Of T)(child))
If result IsNot Nothing Then
Return result
End If
Next
Return Nothing
End Function
You may have a better way. Please post if you do.

editing datagridcell within a DataGridTemplateColumn on key press (key up) event

i have a DataGridTemplateColumn that i created, nothing much
Public Class DataGridAutoCompleteColumn
Inherits DataGridTemplateColumn
Protected Overrides Function PrepareCellForEdit(editingElement As FrameworkElement, editingEventArgs As RoutedEventArgs) As Object
editingElement.MoveFocus(New TraversalRequest(FocusNavigationDirection.First))
Return MyBase.PrepareCellForEdit(editingElement, editingEventArgs)
End Function
End Class
i also have an Auto complete control (which works very well)
and i am using it inside this column like this
<DataGrid.Resources>
<DataTemplate x:Key="EditingDateTemplate">
<Auto:AutoCompleteTextBox FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"
x:Name="EditingBox"
Text="{Binding Path=Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding Source={StaticResource MyComplList}}"/>
</DataTemplate>
<DataTemplate x:Key="NormalCellDataTemplate">
<TextBlock Text="{Binding Path=Name,UpdateSourceTrigger=PropertyChanged,FallbackValue=Add New ...}"/>
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
<Auto:DataGridAutoCompleteColumn
CellTemplate="{StaticResource NormalCellDataTemplate}"
CellEditingTemplate="{StaticResource EditingDateTemplate}"
Header="Test Auto Complete"/>
</DataGrid.Columns>
the focusing mechanism and every thing works well , but i am facing one problem
"Editing"
When double clicking the cell , it goes into Edit mode BUT when i start typing without double clicking it doesn't (not like what the DataGridTextColumn does)
so i made a workaround for this in the keyup event within the datagrid
Private Sub MyDataGrid_KeyUp(sender As Object, e As KeyEventArgs)
Dim ignorablekeys() As Key = {Key.Up, Key.Down, Key.Left, Key.Right, Key.Delete, Key.Return, Key.LeftShift, Key.RightShift, Key.LeftCtrl, Key.RightCtrl, Key.LeftAlt, Key.RightAlt}
If Not ignorablekeys.Contains(e.Key) Then
If TypeOf e.OriginalSource Is DataGridCell Then
Dim cell = DirectCast(e.OriginalSource, DataGridCell)
If Not cell.IsReadOnly Then
Dim datagrid = DirectCast(sender, DataGrid)
datagrid.BeginEdit()
'Push the entered Text Into the cell <<<<< Problem >>>>>
Dim e1 = New KeyEventArgs(e.KeyboardDevice, e.InputSource, e.Timestamp, e.Key)
e1.RoutedEvent = KeyUpEvent
InputManager.Current.ProcessInput(e1)
End If
End If
End If
End Sub
the problem is , it doesn't work , it doesn't send the text

Image not shown in 1st col Listview WPF with VB.NET (1st Col Image, 2nd Col String)

I need expert help on my project.
Please help me, I am newbie to WPF, I am using VB.NET to create application to show pictures from folder chosen by user. on Form I put Button (press to choose folder), Image (to show image chosen from ListView), ListView (2 columns with 1st column image thumbnail and 2nd column the filename).
Please check my XAML code below :
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib" x:Class="MainWindow"
Title="MainWindow" Height="448.545" Width="698.135">
<Grid>
<Image x:Name="ImgView" Margin="259,44,10,72"/>
<Button Content="Pilih Folder" HorizontalAlignment="Left" Height="29" Margin="10,10,0,0" VerticalAlignment="Top" Width="92" Click="Button_Click"/>
<ListView x:Name="ListView1" HorizontalAlignment="Left" Height="351" Margin="10,44,0,0" VerticalAlignment="Top" Width="244">
<ListView.View>
<GridView>
<GridViewColumn Header="Gbr" DisplayMemberBinding="{Binding Gambar}"/>
<GridViewColumn Header="Nama File" DisplayMemberBinding="{Binding NamaFile}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
And here is my Code-behind
Imports System.Windows.Forms
Imports System.Collections.ObjectModel
Class MainWindow
Private _GbrCollection As New ObservableCollection(Of GambarCol)
Public ReadOnly Property GbrCollection() As ObservableCollection(Of GambarCol)
Get
Return _GbrCollection
End Get
End Property
Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
InitializeComponent()
End Sub
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
Dim ImgList() As Image
Dim Img88 As Image
Dim fld As New FolderBrowserDialog
fld.RootFolder = Environment.SpecialFolder.Desktop
'fldDialog.RootFolder = Environment.SpecialFolder.Desktop;
fld.ShowDialog()
'txtPath.Text = fld.SelectedPath
'filesListBox.Items.Clear()
Dim fileNames = My.Computer.FileSystem.GetFiles(fld.SelectedPath, FileIO.SearchOption.SearchTopLevelOnly, "*.jpg")
'Debug.WriteLine(fileNames.Count)
ReDim ImgList(fileNames.Count)
For Each fileName As String In fileNames
_GbrCollection.Add(New GambarCol(Img88, fileName))
Next
ListView1.ItemsSource = GbrCollection
End Sub
Private Sub ListView1_MouseDoubleClick(sender As Object, e As MouseButtonEventArgs) Handles ListView1.MouseDoubleClick
Dim ImgShow As New Image
If ListView1.SelectedItems.Count > 0 Then
End If
'ImgShow.Source = New BitmapImage(New System.Uri(ListView1.Items))
End Sub
Private Sub ListView1_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles ListView1.SelectionChanged
End Sub
End Class
Public Class GambarCol
Private _Gambar As Image
Private _NamaFile As String
Public ReadOnly Property Gambar() As Image
Get
_Gambar = New Image
_Gambar.Source = New BitmapImage(New System.Uri(NamaFile))
_Gambar.Height = 60
Return _Gambar
End Get
End Property
Public ReadOnly Property NamaFile() As String
Get
Return _NamaFile
End Get
End Property
Public Sub New(ByVal GbrImg As Image, ByVal NamaFileNF As String)
_Gambar = GbrImg
_NamaFile = NamaFileNF
End Sub
End Class
Master, help me solve 3 problems :
1. Image can't be shown in column 1 in Listview
2. How to get value from column 2 when user double click the selected row
3. I know my code too complicated , can someone help me to make it simple
Really Really need help
Thank you very much
I just found out the solution, after several references to other articles, this is how I did it.
The XAML code built on StackPanel to hold Image for first col, and second col as usual
<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="409.574" Width="580.224">
<Grid>
<ListView x:Name="ListView1" VirtualizingStackPanel.IsVirtualizing="True" ItemsSource="{Binding ListViewItemsCollections}" Margin="0,56,0,10" HorizontalAlignment="Left" Width="228">
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn x:Name="GridViewColumnName" Header="Picture" Width="90">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image x:Name="Image_GridViewColumnName" Width="90" Height="50" Source="{Binding GridViewColumnNameImageSource}" />
<Label Content="{Binding GridViewColumnNameLabelContent}" Width="50" Height="50" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn x:Name="GridViewColumnLocation" Header="Location" Width="150" DisplayMemberBinding="{Binding GridViewColumnLocation}" />
</GridView>
</ListView.View>
</ListView>
<Button x:Name="btnFolder" Content="Pilih Folder" Margin="10,10,0,0" Height="41" VerticalAlignment="Top" HorizontalAlignment="Left" Width="77"/>
<TextBox x:Name="txtPath" Height="23" Margin="112,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Left" Width="116"/>
<Image x:Name="ImgView" Margin="247,56,10,113"/>
</Grid>
</Window>
And for the code, just use Add method with NEW key, frankly I still don't understand the concept behind New, since WPF real new to me, VB.NET still not all 100% I can code.
For code-behind as follow
Imports System.Windows.Forms.ListViewItem.ListViewSubItemCollection
Imports System.IO
Imports System.Windows.Window
Imports System.Windows.Forms
Imports System.Windows.Forms.FolderBrowserDialog
Class MainWindow
Dim IsiFile() As String
Private Sub btnFolder_Click(sender As Object, e As RoutedEventArgs) Handles btnFolder.Click
Dim fld As New FolderBrowserDialog
Dim iCnt As Integer = 0
fld.RootFolder = Environment.SpecialFolder.Desktop
fld.ShowDialog()
txtPath.Text = fld.SelectedPath
Dim fileNames = My.Computer.FileSystem.GetFiles(fld.SelectedPath, FileIO.SearchOption.SearchTopLevelOnly, "*.jpg")
ReDim IsiFile(fileNames.Count)
For Each fileName As String In fileNames
ListView1.Items.Add(New With { _
Key .GridViewColumnLocation = fileName, _
Key .GridViewColumnNameImageSource = fileName _
})
IsiFile(iCnt) = fileName
iCnt += 1
Next
End Sub
Private Sub ListView1_MouseDoubleClick(sender As Object, e As MouseButtonEventArgs) Handles ListView1.MouseDoubleClick
Dim Gambar As New BitmapImage
Gambar.BeginInit()
Gambar.UriSource = New Uri(IsiFile(ListView1.SelectedIndex))
Gambar.EndInit()
ImgView.Source = Gambar
End Sub
End Class
I hope this helps , thank you

Why Canvas.SetTop animation doesn't work properly?

I have this xaml:
<Canvas Width="75" Height="75">
<Button x:Name="button" Background="Olive" Canvas.Left="0" Canvas.Top="0" Width="75" Height="75" Click="button_Click"/>
</Canvas>
And this code behind:
Private Sub button_Click(ByVal sender as Object, ByVal e as System.Windows.RoutedEventArgs)
Canvas.SetTop(sender, -75)
Dim sb1 As New Storyboard
Dim da1 As New DoubleAnimationUsingKeyFrames
da1.BeginTime = TimeSpan.FromSeconds(0)
Storyboard.SetTargetName(da1, CType(sender, Button).Name)
Storyboard.SetTargetProperty(da1, New PropertyPath(Canvas.TopProperty))
Dim t1 As Double = Canvas.GetTop(sender)
da1.KeyFrames.Add(New SplineDoubleKeyFrame(t1 + 75, TimeSpan.FromSeconds(0.2)))
sb1.Children.Add(da1)
BeginStoryboard(sb1)
End Sub
When I click the button the first time, it properly goes up by 75 and animates back to 0, but when I click the button again, it just animates down by 75. Why is it skipping the Canvas.SetTop line and goes straight to the animating part? And how to fix this?
Use this code (it is in C#) before Canvas.SetTop
(sender as UIElement).BeginAnimation(Canvas.TopProperty, null);
This will avoid that any animation overrides the value Canvas.TopProperty
This link will help you
http://joshsmithonwpf.wordpress.com/2008/08/21/removing-the-value-applied-by-an-animation/

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