Changing Font Icon in WPF using Font Awesome - c#

I am making use of Font Awesome's icons to render basic font images within my C# WPF application. During run-time when I attempt to alter the TextBlock to display a different font icon but the unicode representation is displayed instead of the font icon.
I have created a sample application to display this. When either of the buttons are clicked, it replaces the TextBlock's Text property with the unicode for the respective icon. There is a Resources folder in the project which has the FontAwesome.ttf font file as a Build Resource which the The TextBlock's FontFamily property references.
Here's my sample application's source code:
Code-Behind:
namespace FontAwesomeTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnGlass_Click(object sender, RoutedEventArgs e)
{
tblkFontIcon.Text = "";
}
private void btnMusic_Click(object sender, RoutedEventArgs e)
{
tblkFontIcon.Text = "";
}
private void btnSearch_Click(object sender, RoutedEventArgs e)
{
tblkFontIcon.Text = "";
}
}
}
XAML:
<Window x:Class="FontAwesomeTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Font Awesome Test Window" Height="300" Width="330" Name="FontAwesomeTestWindow">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="25"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Name="btnGlass" Height="35" Width="85" Click="btnGlass_Click" >Glass</Button>
<Button Name="btnMusic" Grid.Column="1" Height="35" Width="85" Click="btnMusic_Click">Music</Button>
<Button Name="btnSearch" Grid.Column="2" Width="85" Height="35" Click="btnSearch_Click">Search</Button>
<TextBlock Grid.Row="1" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="18" FontFamily="../Resources/#FontAwesome"></TextBlock>
<TextBlock Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="18" FontFamily="../Resources/#FontAwesome"></TextBlock>
<TextBlock Grid.Row="1" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="18" FontFamily="../Resources/#FontAwesome"></TextBlock>
<TextBlock Name="tblkFontIcon" Grid.Row="2" Grid.ColumnSpan="3" FontSize="64" FontFamily="../Resources/#FontAwesome" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
</Grid>
I used the following tutorial to incorporate Font Awesome into my application http://www.codeproject.com/Tips/634540/Using-Font-Icons
So in essence, how can I change the Icon but have an Icon display - not Unicode?
Thanks in advance.

Font Awesome has NuGet packages named FontAwesome.UWP and FontAwesome.WPF. Just download one of this.
If you will use a icon import follow namespace into your XAML code:
xmlns:fa="http://schemas.fontawesome.io/icons/"
Use it into your button like this:
<Button x:Name="btnButton">
<Button.Content>
<fa:ImageAwesome Icon="LongArrowLeft"/>
</Button.Content>
</Button>
And finally in your C# code behind:
using FontAwesome.WPF; // on the top of the code
btnButton.Content = FontAwesomeIcon.LongArrowRight;

UPDATE
I found a different post for this topic --
Add Icon font in wpf
I think this should be more likely to what you want.
Make sure your font is added as a resource. Then, use the following
string:
<Setter Property="TextElement.FontFamily" Value="pack://application:,,,/fonts/#FontAwesome" />
In the string above, I'm assuming that the font's name (not the
font's filename) is FontAwesome.
You just need to:
Add the Font to your project, let's say you put them in to a folder "fonts"
Change the Build Action to Resource not Embedded Resource
Add your style to set the font family like the code snip above, and set the TextBlock.Text to the icon you like and apply the style to the TextBlock.
If you want change the icon by updating the TextBlock.Text property, you should set the Text property with the supported unicode string.
Try something like
tblkFontIcon.Text = "\uf000";
rather than
tblkFontIcon.Text = "";
If your're using the code from Using Font Icons
then you probably missed the "How It Works" section in that post.
You should use that markup extension, rather than using the TextBlock.Text property.
In his sample code:
<RibbonButton Label="Import data"
LargeImageSource="{WpfTools:ImageFromFont Text=,
FontFamily=/RibbonFontIconSample;component/Resources/#FontAwesome, Brush=Brown}" />
pay attention to the WpfTools:ImageFromFont, it is the Markup Extention, it allows xaml parser to convert the
{WpfTools:ImageFromFont Text=,
FontFamily=/RibbonFontIconSample;component/Resources/#FontAwesome, Brush=Brown}
to an ImageSource and assigned to the LargeImageSource property.
So in your xaml, you could replace the TextBlock with an Image, then it should be something like:
<Image Source="{WpfTools:ImageFromFont Text=,
FontFamily=/RibbonFontIconSample;component/Resources/#FontAwesome, Brush=Brown}" />
If you want to change the Icon, you will have to change the ImageSource yourself, just follow the Using Font Icons to create your own method, or simply copy the following code from that tutorial.
private static ImageSource CreateGlyph(string text,
FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight,
FontStretch fontStretch, Brush foreBrush)
{
if (fontFamily != null && !String.IsNullOrEmpty(text))
{
Typeface typeface = new Typeface(fontFamily, fontStyle, fontWeight, fontStretch);
GlyphTypeface glyphTypeface;
if (!typeface.TryGetGlyphTypeface(out glyphTypeface))
throw new InvalidOperationException("No glyphtypeface found");
ushort[] glyphIndexes = new ushort[text.Length];
double[] advanceWidths = new double[text.Length];
for (int n = 0; n < text.Length; n++)
{
ushort glyphIndex = glyphTypeface.CharacterToGlyphMap[text[n]];
glyphIndexes[n] = glyphIndex;
double width = glyphTypeface.AdvanceWidths[glyphIndex] * 1.0;
advanceWidths[n] = width;
}
GlyphRun gr = new GlyphRun(glyphTypeface, 0, false, 1.0, glyphIndexes,
new Point(0, 0), advanceWidths,
null, null, null, null, null, null);
GlyphRunDrawing glyphRunDrawing = new GlyphRunDrawing(foreBrush, gr);
return new DrawingImage(glyphRunDrawing);
}
return null;
}

Simple and very easy:
You should install fontawesome font on your system use it as follows
<Button Content="" FontFamily="FontAwesome" FontSize="32" />
How to compose icon, {&#x}{FontAwesome icon code} e.g &#xf2b9 for address book
To find the list of codes goto FontAwesome or astronaut web

Related

How to make a control snap to a Grid.Row/Grid.Column in WPF at runtime?

I have a grid with some ColumnDefinitions and RowDefinitions. What I like to do is drag a control at runtime and have it snap to a given GridColumn/GridRow when the control is over that GridColumn/GridRow. I was not able to find any resources on this. Perhaps I am using the wrong key words. Thanks in advance!
You should extend Grid to handle the drop position. Let the Grid add the dropped element to the appropriate cell.
The following simple but working example shows how to enable dragging of any UIElement from a Panel such as StackPanel or Grid to the custom DrockingGrid.
The custom Grid simply overrides the relevant drag&drop overrides. It's a minimal but working example, therefore only OnDragEnter and OnDrop are overridden.
On drop, you basically have to identify the cell the element was dropped in by using the drop position from the DragEventArgs. Then remove the dropped element from its original parent container (where the drag operation has started) and then insert it into the DockingGrid. You then use Grid.Row and Grid.Column to position the element in the appropriate cell:
DockingGrid.cs
public class DockingGrid : Grid
{
private bool AcceptsDrop { get; set; }
private Brush OriginalBackgroundBrush { get; set; }
public DockingGrid()
{
this.AllowDrop = true;
}
protected override void OnDragEnter(DragEventArgs e)
{
base.OnDragEnter(e);
e.Effects = DragDropEffects.None;
this.AcceptsDrop = e.Data.GetDataPresent(typeof(UIElement));
if (this.AcceptsDrop)
{
e.Effects = DragDropEffects.Move;
ShowDropTargetEffects();
}
}
protected override void OnDragLeave(DragEventArgs e)
{
base.OnDragEnter(e);
ClearDropTargetEffects();
}
protected override void OnDrop(DragEventArgs e)
{
base.OnDrop(e);
if (!this.AcceptsDrop)
{
return;
}
ClearDropTargetEffects();
var droppedElement = e.Data.GetData(typeof(UIElement)) as UIElement;
RemoveDroppedElementFromDragSourceContainer(droppedElement);
_ = this.Children.Add(droppedElement);
Point dropPosition = e.GetPosition(this);
SetColumn(droppedElement, dropPosition.X);
SetRow(droppedElement, dropPosition.Y);
}
private void SetRow(UIElement? droppedElement, double verticalOffset)
{
double totalRowHeight = 0;
int targetRowIndex = 0;
foreach (RowDefinition? rowDefinition in this.RowDefinitions)
{
totalRowHeight += rowDefinition.ActualHeight;
if (totalRowHeight >= verticalOffset)
{
Grid.SetRow(droppedElement, targetRowIndex);
break;
}
targetRowIndex++;
}
}
private void SetColumn(UIElement? droppedElement, double horizontalOffset)
{
double totalColumnWidth = 0;
int targetColumntIndex = 0;
foreach (ColumnDefinition? columnDefinition in this.ColumnDefinitions)
{
totalColumnWidth += columnDefinition.ActualWidth;
if (totalColumnWidth >= horizontalOffset)
{
Grid.SetColumn(droppedElement, targetColumntIndex);
break;
}
targetColumntIndex++;
}
}
private void RemoveDroppedElementFromSourceContainer(UIElement droppedElement)
{
DependencyObject parent = droppedElement is FrameworkElement frameworkElement
? frameworkElement.Parent
: VisualTreeHelper.GetParent(droppedElement);
if (parent is null)
{
return;
}
switch (parent)
{
case Panel panel:
panel.Children.Remove(droppedElement);
break;
case ContentControl contentControl:
contentControl.Content = null;
break;
case ContentPresenter contentPresenter:
contentPresenter.Content = null;
droppedElement.UpdateLayout();
break;
case Decorator decorator:
decorator.Child = null;
break;
default:
throw new NotSupportedException($"Parent type {parent.GetType()} not supported");
}
}
private void ShowDropTargetEffects()
{
this.ShowGridLines = true;
this.OriginalBackgroundBrush = this.Background;
this.Background = Brushes.LightBlue;
}
private void ClearDropTargetEffects()
{
this.Background = this.OriginalBackgroundBrush;
this.ShowGridLines = false;
}
}
Usage
Use it like a normal Grid.
Now the user can drag any control into any of the predefined cells.
<local:DockingGrid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="200" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="300" />
<RowDefinition />
</Grid.RowDefinitions>
</local:DockingGrid>
In the parent host of the drag&drop context for example the Window, enable/start the drag behavior:
MainWindow.xaml.cs
partial class MainWindow : Window
{
protected override void OnPreviewMouseMove(MouseEventArgs e)
{
base.OnPreviewMouseMove(e);
if (e.LeftButton == MouseButtonState.Pressed
&& e.Source is UIElement uIElement)
{
_ = DragDrop.DoDragDrop(uIElement, new DataObject(typeof(UIElement), uIElement), DragDropEffects.Move);
}
}
}
See Microsoft Docs: Drag and Drop Overview to learn more about the feature.
The short answer is to put that control inside something which fills that cell. You could just put it in that grid cell by adding it to the grid children and setting grid row and column attached properties but there is a gotcha.
A grid cell is sort of conceptual.
The grid looks at it's content, looks at it's definitions for rows and columns and works out where to put it's content using measure arrange passes.
Which is a wordy way of saying there's nothing there to drag your control into.
You need a drop target to drag drop anything into. As it's name suggests, you need some sort of a receptacle for the thing you are dragging.
Wpf, however has these things called content controls.
A button actually inherits from content control to allow it to have things like a string in it.
There is also a content control itself. Which is just kind of like a receptacle for something or other.
One of these things can be used in a given cell as a sort of a place holder. And then you have something in a cell that you can drop into.
I think if you just throw a contentcontrol in a grid without anything inside it you might have problems hit testing.
Some experimentation in a scratch project is advisable.
But basically you could have something like:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Fill="Red"
Name="DraggAbleThing"
MouseMove="DraggAbleThing_MouseMove"
/>
<ContentControl Grid.Row="1"
Grid.Column="1"
x:Name="BottomRight"
AllowDrop="True"
>
<Rectangle Fill="Yellow"/>
</ContentControl>
</Grid>
There's a fair bit to implement in order to do drag drop but the idea here is you have something in the bottom right cell which you can drop into. You might have to set ishitestable=false on that yellow rectangle.
I'd have to implement all the drag drop methods to try it out.
If I did and drop works ok then when the contentcontrol gets draggablething dropped into it.
Set the content property of the contentcontrol to draggablething and it is now in the bottom right cell.
It will fill that cell because the grid arranges it's contents to fill whichever logical cell it decides they're "in".
I would like to present an example I wrote that is working.
In the Application I wrote I have a Grid with 4 Rows and 4 Columns.
I can place in each Cell a different UserControl that is based on a class I called
BaseDragDropUserControl:
public class BaseDragDropUserControl: UserControl
{
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.LeftButton == MouseButtonState.Pressed)
{
DataObject data = new DataObject();
data.SetData(DataFormats.StringFormat, nameof(BaseDragDropUserControl));
BaseDragDropUserControl tobemMoved = (BaseDragDropUserControl)e.Source;
int row = (int)tobemMoved.GetValue(Grid.RowProperty);
int col = (int)tobemMoved.GetValue(Grid.ColumnProperty);
data.SetData("Source", tobemMoved);
data.SetData("Row", row);
data.SetData("Col", col);
DragDrop.DoDragDrop(this, data, DragDropEffects.Move);
}
}
protected override void OnGiveFeedback(GiveFeedbackEventArgs e)
{
base.OnGiveFeedback(e);
if (e.Effects.HasFlag(DragDropEffects.Copy))
{
Mouse.SetCursor(Cursors.Cross);
}
else if (e.Effects.HasFlag(DragDropEffects.Move))
{
Mouse.SetCursor(Cursors.Pen);
}
else
{
Mouse.SetCursor(Cursors.No);
}
e.Handled = true;
}
protected override void OnDrop(DragEventArgs e)
{
base.OnDrop(e);
// If the DataObject contains string data, extract it.
if (e.Data.GetDataPresent(DataFormats.StringFormat))
{
string dataString = (string)e.Data.GetData(DataFormats.StringFormat);
if (dataString == nameof(BaseDragDropUserControl))
{
int targetRow = (int)this.GetValue(Grid.RowProperty);
int targetCol = (int)this.GetValue(Grid.ColumnProperty);
int originRow = (int)e.Data.GetData("Row");
int originCol = (int)e.Data.GetData("Col");
BaseDragDropUserControl origin = (BaseDragDropUserControl)e.Data.GetData("Source");
this.SetValue(Grid.RowProperty, originRow);
this.SetValue(Grid.ColumnProperty, originCol);
origin.SetValue(Grid.RowProperty, targetRow);
origin.SetValue(Grid.ColumnProperty, targetCol);
}
}
e.Handled = true;
}
}
The above class is the "Heavy one". It handle both the Drag and the Drop functions.
It ships data object with the origin UserControl and also intercept it when it is dropped. It switch the Grid.Row and Grid.Column values between the origin UserControl and the Target UserControl. In doing this the locations are changed.
I created 2 UsserControls.
RedUserControl and BlueUserControl:
<local:BaseDragDropUserControl x:Class="Problem10.RedUserControl"
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"
xmlns:local="clr-namespace:Problem10"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" AllowDrop="True">
<Grid>
<Rectangle Fill="Red"/>
</Grid>
</local:BaseDragDropUserControl>
<local:BaseDragDropUserControl x:Class="Problem10.BlueUserControl"
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"
xmlns:local="clr-namespace:Problem10"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" AllowDrop="True">
<Grid>
<Rectangle Fill="Blue"/>
</Grid>
</local:BaseDragDropUserControl>
The MainWindow is as following:
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<local:RedUserControl Grid.Row="0" Grid.Column="1"/>
<local:BlueUserControl Grid.Row="0" Grid.Column="0"/>
<local:BlueUserControl Grid.Row="0" Grid.Column="2"/>
<local:BlueUserControl Grid.Row="0" Grid.Column="3"/>
<local:RedUserControl Grid.Row="1" Grid.Column="0"/>
<local:BlueUserControl Grid.Row="1" Grid.Column="1"/>
<local:BlueUserControl Grid.Row="1" Grid.Column="2"/>
<local:BlueUserControl Grid.Row="1" Grid.Column="3"/>
<local:RedUserControl Grid.Row="2" Grid.Column="0"/>
<local:BlueUserControl Grid.Row="2" Grid.Column="1"/>
<local:BlueUserControl Grid.Row="2" Grid.Column="2"/>
<local:BlueUserControl Grid.Row="2" Grid.Column="3"/>
<local:RedUserControl Grid.Row="3" Grid.Column="0"/>
<local:BlueUserControl Grid.Row="3" Grid.Column="1"/>
<local:BlueUserControl Grid.Row="3" Grid.Column="2"/>
<local:BlueUserControl Grid.Row="3" Grid.Column="3"/>
</Grid>
</Window>
The Application is ready for you ! Come and play.

Wpf Xaml DataTemplate. Cannot find source for binding with reference 'ElementName=proxy'

We're in the process of converting a SL5+RIA service to a WPF front and WCF service as a temporary solution.
The old SL solution is maintained with VS 2015 14.0.25431.01. It uses SL5 and .NET 4.0. Other noteworthy libraries are Telerik SL controls 2011.2.0920 and Galasoft MVVMLight SL5 4.1.26.
The WPF solution is made using VS 2019 16.11.15, NET 4.8, Telerik WPF controls 2021.1.325 and Galasoft MVVMLight WPF45 4.1.26.
We have a custom control in which we use bars projected on a timescale to visualise timespans when items have different statusses through a period of time. It's a listbox of the different items and each line is filled with buttons to create the visual of a timebar accross a timeline.
To calculate the widths of the buttons, the total width in both timespan and pixels is used to calculate the comparative width in pixels for each button on each line.
Getting the total width in pixels is giving us problems.
The XAML Datatemplate of the control
<!--Timelines-->
<Grid Grid.Row="0" Grid.Column="1" x:Name="SomeGrid" Background="#FFD3D3D3">
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="10" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!-- TEXTBLOCK TO TRIGGER HELPER CLASS ActualSizePropertyProxy -->
<TextBlock Visibility="Collapsed" x:Name="tb1" Text="{Binding ActualWidthValue, ElementName=proxy}" />
<TextBlock Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left" Text="{Binding ProgramViewmodel.TimelineAreaStart, Source={StaticResource Locator}, StringFormat=\{0:dd-MM-yyyy\}}" />
<TextBlock Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" Text="{Binding ProgramViewmodel.TimelineAreaEnd, Source={StaticResource Locator}, StringFormat=\{0:dd-MM-yyyy\}}" />
<!-- Vertical line under the start date -->
<TextBlock Grid.Row="1" Grid.Column="0" HorizontalAlignment="Left" Text="|" Margin="30 0 0 0" />
<ListBox x:Name="ListBoxTimeFreq"
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Background="#FFD3D3D3"
ItemsSource="{Binding Converter={StaticResource TimefrequentiesFromPAPConverter}}"
SelectionMode="Single"
ItemTemplate="{StaticResource TimeFreqTimelineItemTemplate}">
<ListBox.Resources>
<helpers:ActualSizePropertyProxy Element="{Binding ElementName=ListBoxTimeFreq}" x:Name="proxy" />
</ListBox.Resources>
</ListBox>
</Grid>
It's a simple grid showing the start date, end date and a | marking the start date. Then there is the listbox ListBoxTimeFreq below it that uses the TimeFreqTimelineItemTemplate datatemplate to fill in bars.
And above it all but collapsed there is a textblock that spreads itself across the available space for listbox items, across the available space for timelines. Whenever it's on screen, it triggers the getter on a property ActualWidthValue in a class ActualSizePropertyProxy.
The ActualSizePropertyProxy class
public class ActualSizePropertyProxy : FrameworkElement, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public FrameworkElement Element
{
get { return (FrameworkElement)GetValue(ElementProperty); }
set { SetValue(ElementProperty, value); }
}
public double ActualHeightValue
{
get { return Element == null ? 0 : Element.ActualHeight; }
}
public double ActualWidthValue
{
get
{
if (null != Element)
{
if (Element.ActualWidth != 0.0)
{
ViewModelLocator.ProgramViewmodel.PixelwidthTimelineArea = Element.ActualWidth - 90; // because of CbxValidateStatus, CbxEndOfLine & cbxAddScopeMultiplePAPs
ViewModelLocator.ProgramViewmodel.PixelwidthTimelineAreaScopes = Element.ActualWidth - 86; // because of the border
ViewModelLocator.ProgramViewmodel.Counter = 0;
ViewModelLocator.ProgramViewmodel.CounterScopes = 0;
}
return Element.ActualWidth;
}
else
{
ViewModelLocator.ProgramViewmodel.PixelwidthTimelineArea = 0;
ViewModelLocator.ProgramViewmodel.PixelwidthTimelineAreaScopes = 0;
return 0;
}
}
}
public static readonly DependencyProperty ElementProperty =
DependencyProperty.Register("Element", typeof(FrameworkElement), typeof(ActualSizePropertyProxy),
new PropertyMetadata(null, OnElementPropertyChanged));
private static void OnElementPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((ActualSizePropertyProxy)d).OnElementChanged(e);
}
private void OnElementChanged(DependencyPropertyChangedEventArgs e)
{
FrameworkElement oldElement = (FrameworkElement)e.OldValue;
FrameworkElement newElement = (FrameworkElement)e.NewValue;
newElement.SizeChanged += new SizeChangedEventHandler(Element_SizeChanged);
if (oldElement != null)
{
oldElement.SizeChanged -= new SizeChangedEventHandler(Element_SizeChanged);
}
NotifyPropChange();
}
private void Element_SizeChanged(object sender, SizeChangedEventArgs e)
{
NotifyPropChange();
}
private void NotifyPropChange()
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("ActualWidthValue"));
PropertyChanged(this, new PropertyChangedEventArgs("ActualHeightValue"));
}
}
}
The getter on ActualWidthValue reads out the ActualWidth of the element and stores the value in a viewmodel. Later having the width in that viewmodel is crucial because the converters calculating the sizes for the timebars use that value from the viewmodel.
In the old SL solution this setup causes the PixelwidthTimelineArea in the viewmodel to store a real value. When the application's width is around 1200, the ActualWidthValue return around 800.
But in the new WPF solution, the getter on ActualWidthValue is never triggered. None of the properties or events get triggered in ActualSizePropertyProxy because the binding goes wrong in the XAML in the WPF version.
And we can't figure out the right fix that is needed to make it work in WPF. It wants the source but you can't bind with Source when using ElementName.
Cannot find source for binding with reference 'ElementName=proxy'. BindingExpression:Path=ActualWidthValue; DataItem=null; target element is 'TextBlock' (Name='tb1'); target property is 'Text' (type 'String')
Cannot find source for binding with reference 'ElementName=ListBoxTimeFreq'. BindingExpression:(no path); DataItem=null; target element is 'ActualSizePropertyProxy' (Name='proxy'); target property is 'Element' (type 'FrameworkElement')
It's probably a very easy solution and our heads have gone mush trying to overthink it.

Unknown space while highlighting part of text in a texblock (WPF)

From trial and error I managed to highlight part of text in a textblock which is in a datatemplate of a listbox bounded to a property of a custom class. But the problem now is that when highlighting the text i get a weird unknown space between the highlighted text and the rest of the text.
Here is part of the XAML
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Name="textBox1" TextChanged="textBox1_TextChanged"/>
<ListBox Grid.Row="1" Name="listBox1">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Name="gridOfListbox" Height="25" Margin="0,2">
<DockPanel Name="dockpanelWithTxtBlock">
<TextBlock Name="textbloxk" DockPanel.Dock="Left" FontSize="15" TextAlignment="Center">
<Run Background="Yellow" Text=""></Run>
<Run Text="{Binding ProductID}"></Run>
</TextBlock>
</DockPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
And here part of the code used
ObservableCollection<TItem> items = new ObservableCollection<TItem>();
TItem[] source = new TItem[] { new TItem("Hello"), new TItem("World"), new TItem("System"), new TItem("SystemDefault"), new TItem("SystemFolder") };
And the method for event changedtext
private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
{
string match = textBox1.Text;
foreach (TItem TItem in listBox1.Items)
{
ListBoxItem lbi = (ListBoxItem)this.listBox1.ItemContainerGenerator.ContainerFromItem(TItem);
TextBlock txtBlck = FindFirstElementInVisualTree<TextBlock>(lbi);
Run bold = (Run)txtBlck.Inlines.FirstInline;
Run normal = (Run)txtBlck.Inlines.LastInline;
string s = bold.Text + normal.Text;
if (s.ToLower().StartsWith(match.ToLower()))
{
bold.Text = s.Substring(0, match.Length);
normal.Text = s.Substring(match.Length);
}
else
{
bold.Text = "";
normal.Text = s;
}
}
}
FindFirstElementInVisualTree method is used to find the textboxes needed to search of.
If anymore code is needed let me know.
I also added an image to demonstrate what the problem is.
An help will be appreciated!
Link for image: http://i.stack.imgur.com/rOj0m.png
When you use Run within a TextBlock in XAML, everything not wrapped in <> are considered actual strings. Having a line break would mean a space. Put the two Runs within the same line (without a space in between too).
<TextBlock Name="textbloxk" DockPanel.Dock="Left" FontSize="15" TextAlignment="Center">
<Run Background="Yellow" Text="" /><Run Text="{Binding ProductID}" />
</TextBlock>
Edit
By the way, I just saw your first question which was marked as duplicate. This question is asked correctly; so you should ask questions in this manner in the future.

Windows phone 8 and header with keyboard

So I have a really huge problem:
On my page (It’s about the same as a "messages app" from Microsoft), when I click on my input box and keyboard pop up, my header is moved up and no longer visible.
I’ve searched a little and most of solutions I’ve found were not working (targeting wp7 for them...) . (Like a blog where guy creates a lot of dependency property for then margin of Phoneframe is changed. It works, a little, but the header goes off during animation of keyboard. It’s not enough, it’s really not perfect. )
The Microsoft manages it in standard “Messages” app (With a little bug of font size changing), so it must be possible.
How can realize that ?
I tried this solution and it works just fine :
Try to listen to the TextBox.GotFocus and TextBox.LostFocus events to detect when a TextBox in your application acquires and looses focus.
Put your whole content in a ScrollViewer just as follows :
Code XAML :
<ScrollViewer x:Name="LayoutRoot" Margin="0,0,0,0">
<Grid Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
<TextBlock Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1">
<TextBox HorizontalAlignment="Left" Height="254" Margin="10,183,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="456" GotFocus="TextBox_GotFocus" LostFocus="TextBox_LostFocus"/>
</Grid>
</Grid>
</ScrollViewer>
Adding the content in a ScrollViewer will give the experience of scrolling even when the keyboard is not open, and that's not really desirable.
For that you need to disable scrolling before the Keyboard is opened and after the keyboard is closed.
In the TextBox_GotFocus event play on the top margin of the ScrollViewer :
in the constructor :
public MainPage()
{
InitializeComponent();
LayoutRoot.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
}
the events :
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
LayoutRoot.Margin = new Thickness(0, 330, 0, 0);
LayoutRoot.VerticalScrollBarVisibility = ScrollBarVisibility.Visible;
}
Add the TextBox_LostFocus event handler also to get the page back to its original view when the keyboard is closed :
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
LayoutRoot.Margin = new Thickness(0, 0, 0, 0);
LayoutRoot.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
}
This helps you get the page to its somehow original position when the keyboard is opened.
Hope this helped.
You can make the UI as listbox so that you can scroll the listbox and check the header which is gone up.
When SIP keyboard is rendered, PhoneApplicationFrame.TranslateTransform.Y is set to specific values (-259 in landscape orientation, -339 in portrait orientation). To update layout, we’ll just set top margin to the specified value(-s) and after that Silverlight layout system will fix the issue.
here XAML part:
<Grid x:Name="LayoutRoot" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="WINDOWS PHONE" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="developer's ?" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<Grid Grid.Row="1" Margin="12,0,12,0"></Grid>
<TextBox Grid.Row="2" LostFocus="TextBoxLostFocus"/>
</Grid>
C# portions
private const double LandscapeShift = -259d;
private const double LandscapeShiftWithBar = -328d;
private const double Epsilon = 0.00000001d;
private const double PortraitShift = -339d;
private const double PortraitShiftWithBar = -408d;
public static readonly DependencyProperty TranslateYProperty = DependencyProperty.Register("TranslateY", typeof(double), typeof(MainPage), new PropertyMetadata(0d, OnRenderXPropertyChanged));
public MainPage()
{
InitializeComponent();
Loaded += MainPageLoaded;
}
public double TranslateY
{
get { return (double)GetValue(TranslateYProperty); }
set { SetValue(TranslateYProperty, value); }
}
private static void OnRenderXPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MainPage)d).UpdateTopMargin((double)e.NewValue);
}
private void MainPageLoaded(object sender, RoutedEventArgs e)
{
BindToKeyboardFocus();
}
private void BindToKeyboardFocus()
{
PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
if (frame != null)
{
var group = frame.RenderTransform as TransformGroup;
if (group != null)
{
var translate = group.Children[0] as TranslateTransform;
var translateYBinding = new Binding("Y");
translateYBinding.Source = translate;
SetBinding(TranslateYProperty, translateYBinding);
}
}
}
private void UpdateTopMargin(double translateY)
{
if(IsClose(translateY, LandscapeShift) || IsClose(translateY,PortraitShift) || IsClose(translateY, LandscapeShiftWithBar) || IsClose(translateY, PortraitShiftWithBar))
{
LayoutRoot.Margin = new Thickness(0, -translateY, 0, 0);
}
}
private bool IsClose(double a, double b)
{
return Math.Abs(a - b) < Epsilon;
}
private void TextBoxLostFocus(object sender, RoutedEventArgs e)
{
LayoutRoot.Margin = new Thickness();
}
You can try following link. I think it will be helpful.
http://sorokoletov.com/2011/08/windows-phone-70-handling-text-entry-screens/

Adding Grid/FrameworkElement to ContainerVisual

Well, this might be a strange question. But as title says, can you add Grid to ContainerVisual. Since Grid inherits Visual, I should be able to do it via Children.Add.
Why do I need this? Well, I'm using FlowDocument to print a report. This report needs to have a header, and since Flow Document doesn't support Headers, I decided to add headers during pagination, by using a solution found on the internet.
Also since I don't want to draw entire header by hand but be able to edit it during desing in a designer, I designed it in a separate file as a Grid element.
So my header looks something (I shortened it) like this:
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="2cm" Width="18.7cm">
<Grid.Resources>
<!-- some resources -->
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2cm"/>
<ColumnDefinition/>
<ColumnDefinition Width="2cm"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Grid.RowSpan="2">
<Image Source="Logo.jpg"/>
</Border>
<Border Grid.Column="1" Grid.RowSpan="2">
<StackPanel>
<Label Name="staje" Style="{DynamicResource naslov}"></Label>
<Label Name="predmet" Style="{DynamicResource naslov}"></Label>
</StackPanel>
</Border>
<Border Grid.Column="2" BorderThickness="1">
<StackPanel>
<TextBlock>Datum:</TextBlock>
<Label Name="datum">22. 12. 2013.</Label>
</StackPanel>
</Border>
<Border Grid.Column="2" Grid.Row="1" BorderThickness="1,0,1,1">
<StackPanel>
<TextBlock>Strana:</TextBlock>
<Label Name="strana">
1/2
</Label>
</StackPanel>
</Border>
</Grid>
at each pagination call I load the header using the folowing code:
public FrameworkElement GetHeaderForPage(int Strana)
{
FrameworkElement header = Application.LoadComponent(new Uri("/Header.xaml", UriKind.Relative)) as FrameworkElement;
Label lblTest = LogicalTreeHelper.FindLogicalNode(header, "staje") as Label;
Label lblPredmet = LogicalTreeHelper.FindLogicalNode(header, "predmet") as Label;
Label lblDatum = LogicalTreeHelper.FindLogicalNode(header, "datum") as Label;
Label lblStrana = LogicalTreeHelper.FindLogicalNode(header, "strana") as Label;
lblTest.Content = KakavTest;
lblPredmet.Content = Predmet;
lblDatum.Content = Datum;
lblStrana.Content = string.Format("{0}", Strana);
return header;
}
And finally in the pagination call I place it in the page like so:
DocumentPage page = m_Paginator.GetPage(pageNumber);
// Create a wrapper visual for transformation and add extras
ContainerVisual newpage = new ContainerVisual();
FrameworkElement header = headerGen.GetHeaderForPage(pageNumber);
// header.RenderTransform = new TranslateTransform(0, -header.ActualHeight+10);
ContainerVisual smallerPage = new ContainerVisual();
smallerPage.Children.Add(page.Visual);
//smallerPage.Transform = new MatrixTransform(0.95, 0, 0, 0.95,
// 0.025 * page.ContentBox.Width, 0.025 * page.ContentBox.Height);
newpage.Children.Add(smallerPage);
newpage.Children.Add(header);
newpage.Transform = new TranslateTransform(m_Margin.Left, m_Margin.Top);
RenderTargetBitmap bmp = new RenderTargetBitmap((int)m_PageSize.Width, (int)m_PageSize.Height, 96, 96, PixelFormats.Default);
bmp.Render(newpage);
ImageShow show = new ImageShow(bmp);
show.Show();
return new DocumentPage(newpage, m_PageSize, Move(page.BleedBox), Move(page.ContentBox));
ImageShow class simply opens up new window with an image representing bmp. I was using it to see if the problem was in further processing that is done to display the pages in DocumentViewer. But since ImageShow doesn't display the header Grid, it seems tht I'm doing something terribly wrong.
IN SHORT:
Can you add Grid element to ContainerVisual as a child and have it be drawn correctly. Or do I need to draw it by hand?
In the end I hard coded it all by hand. So no, anything that is higher than VIsual and DrawingVisual, can not be included into ContainerVisual.

Categories

Resources