Introduction
FramewrokElement class Provides a framework-level set of properties, events, and methods
for Windows Presentation Foundation (WPF) elements.
FrameworkElement (FE) derives from UIElement
UIElement is a base class for
WPF core level implementations building on Windows Presentation
Foundation (WPF) elements and basic presentation characteristics.
Background
In this article, i am going to explain how to convert UIElement to ImageSource.
RenderTargetBitmap property of System.Windows.Media used to convert visual object into bitmap.
DrawingVisual used to render vector graphics.
Using the code
I have created TreeView, and binds parent and child treeitems from Collections.
Step 1 :Create Models.
Area.cs
public class Area
{
public string AreaName { get; set; }
public string Description { get; set; }
}
This class is for child nodes.
Location.cs
public class Location
{
public string LocationName { get; set; }
public List<Area> AreaList { get; set; }
}
This class contains properties for Parent tree nodes and child node collections for each parent nodes.
Step 2 :Create ViewModel to bind Collections.
MainViewmodel.cs
public class MainViewmodel
{
private ObservableCollection<Location> _locations;
public ObservableCollection<Location> Locations
{
get { return _locations; }
set { _locations = value; }
}
public MainViewmodel()
{
Locations = new ObservableCollection<Location>();
BindLocationList();
}
List<Area> GetAreaList()
{
List<Area> returnList = new List<Area>();
returnList.Add(new Area() { AreaName = "Kankaria", Description = "The state has witnessed all-round progress in every field..." });
returnList.Add(new Area() { AreaName = "Swapnasrusthi", Description = "The state has witnessed all-round progress in every field..." });
returnList.Add(new Area() { AreaName = "Scien city", Description = "The state has witnessed all-round progress in every field..."});
returnList.Add(new Area() { AreaName = "Lal quila", Description = "The state has witnessed all-round progress in every field..." });
returnList.Add(new Area() { AreaName = "lake view", Description = "The state has witnessed all-round progress in every field..."});
returnList.Add(new Area() { AreaName = "Shilalekh", Description = "The state has witnessed all-round progress in every field..."});
returnList.Add(new Area() { AreaName = "Girnar", Description = "The state has witnessed all-round progress in every field..."});
returnList.Add(new Area() { AreaName = "Zoo", Description = "The state has witnessed all-round progress in every field..."});
returnList.Add(new Area() { AreaName = "chandani chowk", Description = "The state has witnessed all-round progress in every field..."});
returnList.Add(new Area() { AreaName = "Akshradham", Description = "The state has witnessed all-round progress in every field..."});
returnList.Add(new Area() { AreaName = "Qutub minar", Description = "The state has witnessed all-round progress in every field..."});
return returnList;
}
void BindLocationList()
{
List<Area> areaList = GetAreaList();
List<Location> locationList = new List<Location>();
locationList.Add(new Location() { LocationName = "Ahmedabad", AreaList = areaList.Take(3).ToList() });
locationList.Add(new Location() { LocationName = "Jamnagar", AreaList = areaList.Skip(3).Take(2).ToList() });
locationList.Add(new Location() { LocationName = "Junagadh", AreaList = areaList.Skip(5).Take(3).ToList() });
locationList.Add(new Location() { LocationName = "Delhi", AreaList = areaList.Skip(8).Take(3).ToList() });
Locations = new ObservableCollection<Location>(locationList);
}
}
In above code, i have created ObservableCollection of Locations which provides notification on add/edit items.
bind locations and areas within location.
Step 3 : Create Templates for TreeView.
MainWindow.xaml
<DataTemplate x:Key="AreaDataTemplate">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding AreaName}"
VerticalAlignment="Center"
Margin="5"
Style="{StaticResource TextBlockStyle2}" />
<TextBlock Text="{Binding Description}"
Width="300"
Style="{StaticResource TextBlockStyle2}" />
</StackPanel>
</DataTemplate>
DataTemplate provides you with great flexibility to define the presentation of your data.
DataTemplate give you a very flexible and powerful solution to replace the visual appearance of a data item in a control like ListBox, ComboBox or ListView.
Bind child tree items and present layout of child nodes in treeview.
<HierarchicalDataTemplate x:Key="LocationTemplate"
ItemsSource="{Binding AreaList}"
ItemTemplate="{StaticResource AreaDataTemplate}">
<TextBlock Text="{Binding LocationName}"
Margin="5 5 10 10"
Style="{StaticResource TextBlockStyle}" />
</HierarchicalDataTemplate>
HierarchicalDataTemplate Represents a DataTemplat that supports HeaderedItemControl, such as TreeViewItems.
Bind ItmesSource to AreaList(for child TreeViewItems) and ItemTemplate to child DataTemplate resource key.
Step 4 : Place TreeView into Window.
MainWindow.xaml
<Grid Grid.Row="1"
MaxHeight="250">
<TreeView ScrollViewer.CanContentScroll="True"
BorderThickness="0"
Background="#FFF"
ItemsSource="{Binding Locations}"
ItemTemplate="{DynamicResource LocationTemplate}"
x:Name="LocationTreeView" />
</Grid>
I have placed TreeView within Grid and sets its CanContentScroll property to true, which adds scroll when treenode expands if height exceeds 250.
Bind ItemsSource with Locations and set ItemTemplate to HierarchicalDataTemplate.
Step 5 : Place Button, which Excutes code to convert UIElement to ImageSource.
Grid Grid.Row="1">
<Button Content="Generate Image"
x:Name="convert"
Width="100"
Grid.Row="1"
Height="25"
Click="convert_Click" />
</Grid>
Step 6 : Place Image into window to set Source from UIElement.<Grid Grid.Row="2"
MaxWidth="400"
MinHeight="400">
<Image x:Name="displayImage"
Grid.Row="2"
Stretch="Fill"
Margin="0 0 0 30" />
</Grid>
Step 7 : Create Method to find UIElement from parent UIElement. private ScrollViewer GetTemplateChildByName(DependencyObject parent)
{
int childnum = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childnum; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is ScrollViewer)
{
return child as ScrollViewer;
}
else
{
var s = GetTemplateChildByName(child);
if (s != null)
return s;
}
}
return null;
}
This method find UIElement within parent UIElement. it will find child element using GetChild method of VisualTreeHelper.
VisualTreeHelper provides utility methods that perform common tasks involving nodes in a visual tree.
VisualTreeHelper.GetChild method Returns the child visual object from the specified collection index within a specified parent.
I have write this method for find ScrollViewer within TreeView.
Step 8 : Write code to Convert ImageSource from UIElement.
private void convert_Click(object sender, RoutedEventArgs e)
{
ScrollViewer scroll = GetTemplateChildByName(LocationTreeView);
if (scroll != null)
{
ItemsPresenter item = scroll.Content as ItemsPresenter;
double width = item.ActualWidth;
double height = item.ActualHeight;
RenderTargetBitmap bmpCopied = new RenderTargetBitmap((int)Math.Round(width), (int)Math.Round(height), 100, 100, PixelFormats.Default);
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
VisualBrush visualBrush = new VisualBrush(item);
drawingContext.DrawRectangle(visualBrush, null, new Rect(new Point(), new Size(width, height)));
}
bmpCopied.Render(drawingVisual);
displayImage.Source = bmpCopied;
}
}
In Button click event handler, i wrote code to convert TreeView content into ImageSource.
First find ScrollViewer within TreeView.
after that get ItemPresenter from ScrollViewer Content, because we need to convert full expandable TreeView into ImageSource.
if you get ActualHeight of TreeView/ScrollViewer it will gives only visible area height, so it will convert only visible area to image not scroll area.
To convert hided scoll area as well as visible area, you have to get ScrollViewer Content as a ItemPresenter and Draw Image using ItemPresenter element and its ActualWidth/ActualHeight property.
Points of Interest
when you work with ScrollViewer,
Always use its content area in width and height calculation.
it will gives ActualHeight/ActualWidth of content within ScrollViewer.