Setting image in runtime WPF - wpf

I have a template in app.xaml. During runtime, i want to create a button and apply this template. I also want to set the Image source during runtime.
<Application.Resources>
<ControlTemplate x:Key="btemp2" TargetType="{x:Type Button}">
<Image x:Name="myimage" HorizontalAlignment="Center" Height="84" VerticalAlignment="Center" Width="100" Margin="10,-17.5,7,-24.5" Stretch="UniformToFill"/>
</ControlTemplate>
</Application.Resources>
runtime code:
Button newButton = new Button();
newButton.Width = 100;
newButton.Height = 50;
newButton.Template = (ControlTemplate)TryFindResource("btemp2");
System.Windows.Controls.Image i = newButton.Template.FindName("myimage",this) as System.Windows.Controls.Image;
Bitmap bmp = GetIconImageFromFile(fileName);
BitmapSource src = GetBitmapImageFromBitmap(bmp);
i.Source = src;
stack.Children.Add(newButton);
It does not work as expected.
Breakpoint does not reach
Bitmap bmp = GetIconImageFromFile(fileName);

You can use Binding to set image. So you should change ControlTemplate. In that example we using Button Tag property to set image Source.
<ControlTemplate x:Key="btemp2" TargetType="{x:Type Button}">
<Image x:Name="myimage" HorizontalAlignment="Center" Height="84" VerticalAlignment="Center" Width="100" Margin="10,-17.5,7,-24.5" Stretch="UniformToFill"
Source="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Tag}"/>
</ControlTemplate>
And the Button creating code should look like this.
Button newButton = new Button();
newButton.Width = 100;
newButton.Height = 50;
newButton.Template = ( ControlTemplate )TryFindResource( "btemp2" );
tempGrid.Children.Add( newButton );
BitmapImage image = new BitmapImage(new Uri("pack://application:,,,/WPFTest;component/Images/GPlus.png"));
newButton.Tag = image;

Remove this , and use newButton in code below, and handle Loaded event :
Grd.Children.Add(newButton);
newButton.Loaded += newButton_Loaded;
...
void newButton_Loaded(object sender, RoutedEventArgs e)
{
Image img = (Image)newButton.Template.FindName("myimage", newButton);
...
}

Related

Label control - Border lines issue on PDF export

I attempted to export a Label control with a white background and noticed some border lines in the output file. Is there a workaround for this problem?
UI As seen below:
Exported output
Xaml Code
<Canvas x:Name="sampleCanvas" Grid.Row="1" Grid.Column="0">
<Canvas>
<Grid x:Name="SimpleNode" Height="50" Width="50" Margin="200,200" Background="CornflowerBlue">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<Canvas>
<Border x:Name="Bd" BorderThickness="1"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Border BorderThickness="1" Margin="2">
<Grid Name="parent">
</Grid>
</Border>
</Border>
<Label x:Name="lbl" BorderBrush="Transparent" Background="#FFFF" BorderThickness="0"
Content="Node Text" FontSize="12" FontFamily="Calibri"
Canvas.Top="40"
Panel.ZIndex="2" HorizontalAlignment="Center">
</Label>
</Canvas>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</Grid>
</Canvas>
</Canvas>
Code Used for Printing
private void Button_Click(object sender, RoutedEventArgs e)
{
var filename = SaveCurrentDocument();
PrintDialog printDialog = new PrintDialog();
if ((bool)printDialog.ShowDialog())
{
PrintXPSDocument(printDialog, filename);
}
}
public string SaveCurrentDocument()
{
var filename = #"D:\exportWPF.xps";
File.Delete(filename);
using (Stream stream = File.Create(filename))
{
this.InvokeXps(stream);
}
return filename;
}
private void InvokeXps(Stream stream)
{
FixedDocument fixedDoc = new FixedDocument();
PageContent pageContent = new PageContent();
FixedPage fixedPage = CreateFixedPage(new Rect(0, 0, 500, 500));
((System.Windows.Markup.IAddChild)pageContent).AddChild(fixedPage);
fixedDoc.Pages.Add(pageContent);
Package package = Package.Open(stream, FileMode.Create, FileAccess.ReadWrite);
XpsDocument doc = new XpsDocument(package);
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);
writer.Write(fixedDoc);
doc.Close();
package.Close();
}
internal FixedPage CreateFixedPage(Rect area)
{
VisualBrush visualBrush = new VisualBrush(sampleCanvas);
System.Windows.Shapes.Rectangle rect = new System.Windows.Shapes.Rectangle();
RenderOptions.SetBitmapScalingMode(visualBrush, BitmapScalingMode.HighQuality);
visualBrush.Stretch = Stretch.None;
visualBrush.ViewboxUnits = BrushMappingMode.Absolute;
visualBrush.Viewbox = area;
rect.Fill = visualBrush;
rect.Stretch = Stretch.Fill;
rect.Width = area.Width;
rect.Height = area.Height;
rect.Arrange(new Rect(area.Size));
FixedPage fp = new FixedPage();
fp.Width = area.Width;
fp.Height = area.Height;
fp.Children.Add(rect);
fp.Arrange(new Rect(area.Size));
return fp;
}
internal void PrintXPSDocument(PrintDialog printDialog, string filePath)
{
var document = new XpsDocument(filePath, FileAccess.Read);
var sequence = document.GetFixedDocumentSequence();
FixedDocument fixedDocument = sequence.References[0].GetDocument(false);
fixedDocument.DocumentPaginator.PageSize = new System.Windows.Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight);
printDialog.PrintDocument(fixedDocument.DocumentPaginator, "Printing");
document.Close();
}
I attempted to export my canvas control to.xps format and then print it using the Print Dialog via the shared code.
If you just want text then you should use a textblock rather than label.
Instead of
<Label x:Name="lbl" BorderBrush="Transparent" Background="#FFFF" BorderThickness="0"
Content="Node Text" FontSize="12" FontFamily="Calibri"
Canvas.Top="40"
Panel.ZIndex="2"
HorizontalAlignment="Center">
</Label>
You can use a textblock
<TextBlock x:Name="justText"
Background="#FFFF"
Text="Node Text"
FontSize="12"
FontFamily="Calibri"
Canvas.Top="40"
Panel.ZIndex="2"
TextAlignment="Center">
</TextBlock>
The functionality is very similar.
When you set content of a label to a string, a label creates a textblock as it's content and sets text to the string.
If you need the white background then you could try setting the height and width of the textblock but it seems you already have a white background anyhow.

WPF dynamically create button by style and set control elements inside

We have a style defined as follow:
<Style x:Key="StartButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Button ... Style="{StaticResource StartBtnStyle}">
<Button.Content>
<StackPanel>
<TextBlock x:Name="Line1" Text="..." FontSize="20" />
<TextBlock x:Name="Line2" Text="..." FontSize="8" />
</StackPanel>
</Button.Content>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
We creates a button dynamically:
var button = new Button() {
Margin = new Thickness(3d,3d,3d,10d),
Style = FindResource("StartButtonStyle") as Style,
};
We want to find the "Line1" textblock inside the new button, and set the Text property:
var line1 = (TextBlock)button.FindName("Line1");
But it finds only "null" :( How should we find the textblock inside the button? Any advice is appreciated! Thanks in advance!
Wait until the Style has been applied - there is no TextBlock elment before this - to the Button and then find the TextBlock in the visual tree:
var button = new Button()
{
Margin = new Thickness(3d, 3d, 3d, 10d),
Style = FindResource("StartButtonStyle") as Style,
};
button.Loaded += (s, e) =>
{
TextBlock line1 = FindChild<TextBlock>(button, "Line1");
if(line1 != null)
{
line1.Text = "...";
}
};
The recursive FindChild<T> method is from here.

How to create custom button from custom control?

I'm testing some codes about custom control. I had the following styles defines in Themes folder.LayerGrid.xaml.A button with an image and text. This PanelButtonStylestyle is used in layergrid.cs.
<Style TargetType="{x:Type common:LayerGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<DockPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch" LastChildFill="True"
Name="PART_ParentPanel">
<DockPanel.Resources>
<Style x:Key="PanelButtonStyle" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True"></Setter>
<Setter Property="Focusable" Value="False"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="BorderPath" Margin="0"
BorderThickness="0" Background="{StaticResource TabItemBackgroundBrushUnselected}"
BorderBrush="{StaticResource TabItem_BorderBrush_Selected}">
<StackPanel Orientation="Horizontal">
<Image Source="/MCLF;component/Images/图像 3.png" Width="15" Height="15"
HorizontalAlignment="Center " VerticalAlignment="Center"></Image>
<TextBlock TextTrimming="CharacterEllipsis"
Text ="{TemplateBinding Name}"></TextBlock>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DockPanel.Resources>
<StackPanel Name="PART_BottomCntl" Orientation="Horizontal" DockPanel.Dock="Bottom" Background="AliceBlue"></StackPanel>
<StackPanel Name="PART_LeftCntl" Orientation="Horizontal" DockPanel.Dock="Left" Background="AliceBlue">
<StackPanel.LayoutTransform>
<RotateTransform Angle="90"/>
</StackPanel.LayoutTransform>
</StackPanel>
<StackPanel Name="PART_RightCntl" Orientation="Horizontal" DockPanel.Dock="Right" Background="AliceBlue">
<StackPanel.LayoutTransform>
<RotateTransform Angle="90"/>
</StackPanel.LayoutTransform>
</StackPanel>
<Grid Name="PART_MasterGrid" IsSharedSizeScope="True" Background="AliceBlue"></Grid>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
private Button AddToColumnStackPanel(Layer layer)
{
var btn = new Button
{
//Background = Brushes.Transparent,
//BorderBrush = Brushes.Transparent,
//BorderThickness = new Thickness(0),
//Height = 22,
//MinWidth = 55.0,
Padding = new Thickness(10, 0, 10, 0),
//FontWeight = FontWeights.Bold,
Style = (Style)PART_MasterGrid.FindResource("PanelButtonStyle"),
};
btn.Click += (o, e) =>...
}
And the DP Layer.Name is determined in MainWindow.xaml with
<controls:Layer Level="1" Orientation="Column" Name="Symbols" ColumnLocation="Left">
<controls:Layer.Content>
<Grid>
<Grid.DataContext>
<vm:MainViewModel/>
</Grid.DataContext>
</Grid>
</controls:Layer.Content>
Now the problem is the DP Name=Symbols is not bind correctly into the button PanelButtonStyle
I read a similar post but that example set the whole target type to the DP somecustomControlWPF Custom Control: TemplateBinding to Image
Update: The DP Name is in class Layer,which is used to define the properties of each layer's location, orientation, name, content etc... The class LayerGrid serves as the class backing the custom control.
In LayerGrid.cs we have:
public class LayerGrid : ContentControl
{
...
}
public class Layer : UIElement
{
public enum LayerOrientation
{
Row,
Column
}
public enum LayerColumnLocation
{
Left,
Right
}
public static readonly DependencyProperty LevelProperty;
public static readonly DependencyProperty ContentProperty;
public static readonly DependencyProperty OrientationProperty;
public static readonly DependencyProperty NameProperty;
public static readonly DependencyProperty ColumnLocationProperty;
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
static Layer()
{
NameProperty = DependencyProperty.Register(
"Name",
typeof(string),
typeof(Layer));
...
}
}
And LayerGrid.xaml serves the purpose of laying out the general panels; Each button will be added to the corresponding stackPanel such as PART_LeftCntl by calling the function above.
<DockPanel>
<StackPanel Name="PART_BottomCntl" Orientation="Horizontal" DockPanel.Dock="Bottom" Background="AliceBlue"></StackPanel>
<StackPanel Name="PART_LeftCntl" Orientation="Horizontal" DockPanel.Dock="Left" Background="AliceBlue">
<StackPanel.LayoutTransform>
<RotateTransform Angle="90"/>
</StackPanel.LayoutTransform>
</StackPanel>
<StackPanel Name="PART_RightCntl" Orientation="Horizontal" DockPanel.Dock="Right" Background="AliceBlue">
<StackPanel.LayoutTransform>
<RotateTransform Angle="90"/>
</StackPanel.LayoutTransform>
</StackPanel>
<Grid Name="PART_MasterGrid" IsSharedSizeScope="True" Background="AliceBlue"></Grid>
</DockPanel>
And MainWindow.xaml is responsible for giving the contents inside;
Update2 Below an example of the same style is attached.Only Buttonstyle is changed.https://www.dropbox.com/sh/os34tr8zl21uj4o/AAC_segoCWzAbJMFzCKHyZnpa?dl=0
//var btn = new Button
//{
// Background = Brushes.Transparent,
// BorderBrush = Brushes.Transparent,
// BorderThickness = new Thickness(0),
// Height = 22,
// MinWidth = 65.0,
// Padding = new Thickness(10, 0, 15, 0),
// FontWeight = FontWeights.Bold,
// Style = (Style)PART_MasterGrid.FindResource("buttonStyle"),
// Content = layer.Name
//};
var btn = new Button
{
Padding = new Thickness(10, 0, 10, 0),
//FontWeight = FontWeights.Bold,
Style = (Style)PART_MasterGrid.FindResource("PanelButtonStyle")
//Content = layer.Name
};
I think you are looking for how to create the binding in code behind.
So this should work for you
Binding b = new Binding("Name");
b.Source = layer;
btn.SetBinding(Button.ContentProperty, b);
so remove Content = layer.Name & add this code before btn.Click += (o, e) =>...
give it a try and see if this is what you are looking for
EDIT
After looking at your implementation I found that the buttons are directly added to parts (StackPanel) of the layered grid template instead of layer (see below). So Relative source may not help here.
However, there are ways how you can achieve your goal in this scenario. As an easy option you can leverage Content property of Button.
So start by binding the Text property it to the template's Content property
<TextBlock TextTrimming="CharacterEllipsis" FontSize="10"
Text ="{TemplateBinding Content}"></TextBlock>
then in the code you can simply use Content = layer.Name if the Name is not supposed to change.
eg
var btn = new Button
{
Padding = new Thickness(10, 0, 10, 0),
Style = (Style)PART_MasterGrid.FindResource("PanelButtonStyle"),
Content = layer.Name
};
Or alternatively you can bind Name with Content property to reflect the changes if needed.
eg
var btn = new Button
{
Padding = new Thickness(10, 0, 10, 0),
Style = (Style)PART_MasterGrid.FindResource("PanelButtonStyle")
};
Binding b = new Binding("Name");
b.Source = layer;
btn.SetBinding(Button.ContentProperty, b);
Let me know if this helps.

Binding chart LegendItem checkbox to series visibility in WPF in C# codebehind

I have multiple column series chart getting generated in C#. I am further trying to get the legend for this chart with the checkboxes. Such that the chart displays the column series for only legend items that are checked.
I need to do this in C# code behind and not in HTML.
I have the below existing code that creates the multiple dynamic column series -
foreach (KeyValuePair<int, string> item in list)
{
foreach (System.Data.DataRow dRow in dtTable.Rows)
{
<formation of listSource>
}
ColumnSeries ser = new ColumnSeries { Title = item.Value, IndependentValueBinding = new Binding("Key"), DependentValueBinding = new Binding("Value") };
ser.ItemsSource = null;
ser.ItemsSource = listSource;
ser.DataPointStyle = columnStyleBrown;
mcChart.Series.Add(ser);
i++;
}
}
And I further want to add something to -
ser.LegendItemStyle =
So I need to know how to create a legend style with the check boxes in c#.
There can be 2 ways of achieving this-
Either by modifying the existing legend to contain check boxes also (preferred)
Or to create a new legend altogether
Can anyone please help?
Thanks in advance!
Was able to resolve this -
xaml code -
<Grid Name="LayoutRoot">
<Grid.Resources>
<Style x:Key="CategoryLegendItem" TargetType="DVC:LegendItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DVC:LegendItem">
<StackPanel Orientation="Horizontal">
<CheckBox VerticalAlignment="Center" IsChecked="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Owner.Visibility, Mode=TwoWay, Converter={StaticResource BooleanToVisibilityConverter1}}" Margin="0,0,3,0" />
<Rectangle Width="8" Height="8" Fill="{Binding Background}" Stroke="{Binding BorderBrush}" StrokeThickness="1" Margin="0,0,3,0" />
<DV:Title VerticalAlignment="Center" Content="{TemplateBinding Content}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<DVC:Chart Name="mcChart" >
</DVC:Chart>
Relevant C# code for dynamic column series -
ColumnSeries ser = new ColumnSeries { Title = kvpNuclide.Value, IndependentValueBinding = new Binding("Key"), DependentValueBinding = new Binding("Value") };
ser.ItemsSource = null;
ser.ItemsSource = listRelease;
ser.DataPointStyle = columnStyleAqua;
ser.LegendItemStyle = (Style)LayoutRoot.Resources["CategoryLegendItem"];
mcChart.Series.Add(ser);

When text wraps within TextBlock, ActualHeight is incorrect

I have a TextBlock with a Border around it that's inside of a Canvas that I'm using to animate it as part of a custom control. The block slides in from the bottom of the screen over the top of the image. I'm trying to use the ActualHeight of the TextBlock to determine how far to move it onto the page, but when there is so much text that it wraps to two lines, the ActualHeight returns the same size as though there was a single line.
TextBlock:
<DataTemplate DataType="{x:Type contentTypes:BusinessAdText}" x:Key="BusinessAdTextTemplate">
<Border Background="#a9a9a975"
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type Canvas}}, Path=ActualWidth}">
<TextBlock Margin="20" Text="{Binding Text}"
TextWrapping="Wrap">
</TextBlock>
</Border>
</DataTemplate>
This style is applied which has the canvas:
<Style TargetType="local:BusinessAd">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:BusinessAd">
<Border Background="Transparent">
<Canvas ClipToBounds="True">
<ContentPresenter x:Name="PART_Content"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</Canvas>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Code behind for BusinessAd.cs has:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_contentPart = GetTemplateChild("PART_Content") as FrameworkElement;
}
Then just using a simple DoubleAnimation I move it onto the screen:
if (_contentPart != null && _isLoaded)
{
_storyboard.Stop();
vAnimation.From = ActualHeight;
vAnimation.To = ActualHeight - _contentPart.ActualHeight;
//_contentPart.ActualHeight returns 46.something no matter how much text is there
vAnimation.Duration = new Duration(TimeSpan.FromSeconds(Duration));
if (_storyboard.Children.Count == 0)
{
_storyboard.Children.Add(vAnimation);
Storyboard.SetTargetProperty(vAnimation, new PropertyPath("(Canvas.Top)"));
Storyboard.SetTarget(vAnimation, _contentPart);
}
_storyboard.Begin();
}
You have to call UpdateLayout() before checking ActualHeight:
if (_contentPart != null && _isLoaded)
{
_storyboard.Stop();
UpdateLayout();
vAnimation.From = ActualHeight;
vAnimation.To = ActualHeight - _contentPart.ActualHeight;
//_contentPart.ActualHeight returns 46.something no matter how much text is there
vAnimation.Duration = new Duration(TimeSpan.FromSeconds(Duration));
if (_storyboard.Children.Count == 0)
{
_storyboard.Children.Add(vAnimation);
Storyboard.SetTargetProperty(vAnimation, new PropertyPath("(Canvas.Top)"));
Storyboard.SetTarget(vAnimation, _contentPart);
}
_storyboard.Begin();
}
I'm not sure if this applies to you, but for me, the textblock in Windows.UI.Xaml.Controls needs to be preceded with this:
myTextBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
Before, when I had just myTextBlock.Measure(new Size());, it worked when there wasn't any text wrapping, but with wrapping ActualWidth and ActualHeight returned the dimensions of the word/letter, depending on WrapWholeWords or Wrap

Resources