Make wpf button fonts match at runtime - wpf

I have several buttons, like this:
<Button Name="btnContent" Grid.Column="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" >
<Viewbox MaxWidth="100" StretchDirection="Both">
<TextBlock Text="Content" ></TextBlock>
</Viewbox>
</Button>
<Button Name="btnMoreContent" Grid.Column="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" >
<Viewbox MaxWidth="100" StretchDirection="Both">
<TextBlock Text="More Content" ></TextBlock>
</Viewbox>
</Button>
The font scales to fit the buttons. How can I grab the text size / scale value of the button with the smallest text at runtime and set them all to that size?
I tried:
double FontSize = btnContentButton.FontSize;
if (FontSize < btnLongerContentButton.FontSize)
{
FontSize = btnLongerContentButton.FontSize;
}
btnContentButton.FontSize = FontSize;
btnLongerContentButton.FontSize = FontSize;
But this doesn't work, because I never actually changed the font size - It sets them all to 12.

Try the suggested solutions in this link and see if they can solve the problem.
However, you might want to try this one too: Handle Load event, find all TextBlocks with parent of type ViewBox (You might check other conditions or set proper names, to avoid further problems here), adjust their Width, properly, based on the Width of the one with the longest Text:
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
List<TextBlock> tbs = new List<TextBlock>();
TextBlock longestText = null;
foreach (TextBlock t in FindVisualChildren<TextBlock>(this))
{
if (t.Parent is Viewbox)
{
tbs.Add(t);
if (longestText == null)
longestText = t;
else if (t.Text.Length > longestText.Text.Length)
longestText = t;
}
}
double a = longestText.ActualWidth / longestText.ActualHeight;
foreach (TextBlock tb in tbs)
{
if (tb == longestText)
continue;
tb.Width = a * tb.ActualHeight;
}
}
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
FindVisualChildren method is from here.
Hope it helps.

Related

WPF Rich text box underline

I have strange problem with my richtextbox.
I want to detect when text is Bold, Italic etc.
E.g
if (richTextBox.Selection.GetPropertyValue(TextElement.FontStyleProperty).ToString() == "Italic") // Pochylenie
{
heremycode
}
If we use
MessageBox(richTextBox.Selection.GetPropertyValue(TextElement.FontStyleProperty).ToString());
I get Italic. I want do exacly the same with underline and strikethrough, because I can't use
TextBlock.TextDecorationsProperty.ToString(),
because i get only name of method i think? Nothink like "italic", or "bold" just "FontStyleProperty".
private void richTextBox_SelectionChanged(object sender, RoutedEventArgs e)
{
....
if (richTextBox.Selection.GetPropertyValue(TextElement.FontStyleProperty).ToString() == "Italic"
{
backgroundP.Stroke = Brushes.Black;
backgroundP.Fill = Brushes.LawnGreen;
p = true;
}
TextRange selectionRange = new TextRange(richTextBox.Selection.Start, richTextBox.Selection.End);
if (selectionRange.GetPropertyValue(Underline.TextDecorationsProperty).Equals(TextDecorations.Underline))
{
MessageBox.Show("Wow We did it :)");
backgroundUnderline.Stroke = Brushes.Black;
}
}
And XAML Code:
<Grid x:Name="Center" Margin="10,231,10,10">
<Rectangle Fill="#B2F4F4F5" Stroke="Black" Margin="1,0,-1,0"/>
<Label x:Name="labelNotepad" Content="Notepad" HorizontalAlignment="Left" VerticalAlignment="Top" Width="385" FontWeight="Bold" Background="#FFC1FCFF" FontSize="21.333" Height="56" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Margin="2,1,0,0" BorderThickness="0,0,1,0"/>
<RichTextBox x:Name="richTextBox" Margin="1,58,0,0" FontSize="16" BorderThickness="1,2,1,1" BorderBrush="Black" UseLayoutRounding="False" VerticalScrollBarVisibility="Auto" Background="#7FFFFFFF" SelectionChanged="richTextBox_SelectionChanged">
<FlowDocument/>
</RichTextBox>
Have you tried this:
TextRange selectionRange = new TextRange(RichTextControl.Selection.Start, RichTextControl.Selection.End);
if (selectionRange.GetPropertyValue(Inline.TextDecorationsProperty) == TextDecorations.Underline)
{
}
this should work, you get only the Object name beacause you don´t set the dependency property what you want to have as a string. The decoration can be Underline etc you must declarate what you want to check
We need to see more of your code because SeeuD1's suggestion should work. However, it will only work if the entire selection is underlined.
If you need to see if there is ANY underlined text in the selection, not ONLY, then you need to check all the inline objects in the selection.
In this example I will only check paragraphs in your FlowDocument:
foreach (var block in RichTextBox.Document.Blocks.Where(x => selection.Start.CompareTo(x.ContentEnd) < 0 && selection.End.CompareTo(x.ContentStart) > 0))
{
var paragraph = block as Paragraph;
if (paragraph != null)
{
foreach (var selectedInline in paragraph.Inlines.Where(x => selection.Start.CompareTo(x.ContentEnd) < 0 && selection.End.CompareTo(x.ContentStart) > 0))
{
if (selectedInline.GetPropertyValue(Inline.TextDecorationsProperty) == TextDecorations.Underline)
{
MessageBox.Show("Wow We did it :)");
}
}
}
}

why the name of the Controls in ItemsControl not displaying in Codebehind file in Wpf

i Created simple sample using the ItemsControl and DataTemplate.. i want to bind the values using the C# code in textblocs.But i didn't get the textblock names and Datatemplate names in Code behind file please tell me why.. What to do to get the names of the controls ?
<ItemsControl ItemsSource="{Binding Path=.}" >
<ItemsControl.ItemTemplate>
<DataTemplate x:Name="datatemp">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="Textblock1" Text="{Binding }" FontWeight="Bold" ></TextBlock>
<TextBlock Text=", " />
<TextBlock Text="{Binding }" x:Name="Textblock2"></TextBlock>
<TextBlock Text=", " />
<TextBlock Text="{Binding }" x:Name="Textblock3"></TextBlock>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
but here in Code file Textblock1 and other names not dispalying even i used the "Name" only Instead of "x:Name "
No member will be generated for the DataTemplate. A datatemplate is just used for instantiating items dynamically at runtime, so the Controls don't even exist until you add items to your ItemsControl, and even then i think the names of individual controls inside the DataTemplate are just useful for usage from inside the DataTemplate's markup.
You can generate names, if needed when the ItemsControl is loaded.
private void OnPopupItemsLoaded(object sender, RoutedEventArgs e)
{
var itemsControl = sender as ItemsControl;
itemsControl.ApplyTemplate();
var numItems = itemsControl.ItemContainerGenerator.Items.Count();
for (var i = 0; i < numItems; i++)
{
var container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i);
textBlock = FindVisualChild<TextBlock>(container);
if (textBlock != null)
{
textBlock.Name = SanitizeName(textBlock.Text);
textBlock.Uid = $"Item{i}";
}
}
}
private static T FindVisualChild<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
return (T)child;
}
T childItem = FindVisualChild<T>(child);
if (childItem != null)
{
return childItem;
}
}
}
// None found
return null;
}
// FrameworkeElement.Name must start with an underscore or letter and
// only contain letters, digits, or underscores.
private string SanitizeName(string textString)
{
// Text may start with a digit
var sanitizedName = "_";
foreach (var c in textString)
{
if (char.IsLetterOrDigit(c))
{
sanitizedName += c;
}
else
{
sanitizedName += "_";
}
}
return sanitizedName;
}

How do I access a named Control within my ItemPanelTemplate from the code-behind?

I am trying to access a Canvas object within an ItemsPanelTemplate so I can add items directly to it in the code-behind.
Here is my XAML:
<ListBox x:Name="MyMap" ItemsSource="{Binding MapItems}"
Background="Gray"
SelectionChanged="MyMap_SelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas x:Name="MyMapCanvas"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Background="Transparent">
</Canvas>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
How can I access "MyMapCanvas" in the code-behind to add items to it?
I have tried the following:
Canvas canvas = (Canvas)MyMap.ItemsPanel.LoadContent();
and
ContentPresenter cp = (ContentPresenter)(VisualTreeHelper.GetChild(MyMap, 0));
ItemsPanelTemplate ipt = MyMap.ItemsPanel;
Canvas canvas = ipt.FindName("MyMapCanvas", cp) as Canvas;
Thanks in advance.
Try this:
//your OnLoaded handler
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
Canvas canvas = FindVisualChild<Canvas>(MyMap);
}
public TChildItem FindVisualChild<TChildItem>(DependencyObject obj) where TChildItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
var child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is TChildItem)
return (TChildItem)child;
var childOfChild = FindVisualChild<TChildItem>(child);
if (childOfChild != null)
return childOfChild;
}
return null;
}

Enumerate ItemsControl.Items as UIElements

I have a list of hyperlinks that are displayed through an ItemsControl, something like this:
<ItemsControl x:Name="SubMenu" Visibility="Collapsed">
<ItemsControl.ItemTemplate>
<DataTemplate>
<HyperlinkButton Content="{Binding Name}"
NavigateUri="{Binding Url}"
TargetName="ContentFrame"
Style="{StaticResource LinkStyle}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Style="{StaticResource LinksStackPanelStyle}"
VerticalAlignment="Center"
HorizontalAlignment="Left" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
what I need to do is enumerate the actual hyperlinks in the submenu, something like this:
foreach (UIElement child in SubMenu.Items) // this does not work!
{
HyperlinkButton hb = child as HyperlinkButton;
if (hb != null && hb.NavigateUri != null)
{
if (hb.NavigateUri.ToString().Equals(e.Uri.ToString()))
{
VisualStateManager.GoToState(hb, "ActiveLink", true);
}
else
{
VisualStateManager.GoToState(hb, "InactiveLink", true);
}
}
}
The problem is that I can´t seem to find a way to enumerate the actual UI elements in the ItemsCollection.Items.
Anyone know how to do this or a possible workaround?
I can mention that what I´m trying to do is build a menu and submenu that display the hyperlinks clicked as a sort of breadcrumb.
UPDATE:
The best thing would be if I could get to that stackpanel somehow because this code seems to work:
foreach (UIElement child in LinksStackPanel.Children)
{
HyperlinkButton hb = child as HyperlinkButton;
if (hb != null && hb.NavigateUri != null)
{
if (hb.NavigateUri.ToString().Equals(e.Uri.ToString()))
{
VisualStateManager.GoToState(hb, "ActiveLink", true);
}
else
{
VisualStateManager.GoToState(hb, "InactiveLink", true);
}
}
}
The solution looks like this:
foreach (var item in SubMenu.Items)
{
var hb = SubMenu.ItemContainerGenerator.ContainerFromItem(item).FindVisualChild<HyperlinkButton>();
if (hb.NavigateUri.ToString().Equals(e.Uri.ToString()))
{
VisualStateManager.GoToState(hb, "ActiveLink", true);
}
else
{
VisualStateManager.GoToState(hb, "InactiveLink", true);
}
}
The extension method FindVisualChild:
public static T FindVisualChild<T>(this DependencyObject instance) where T : DependencyObject
{
T control = default(T);
if (instance != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(instance); i++)
{
if ((control = VisualTreeHelper.GetChild(instance, i) as T) != null)
{
break;
}
control = FindVisualChild<T>(VisualTreeHelper.GetChild(instance, i));
}
}
return control;
}
Try using the ItemContainerGenerator.ContainerFromItem method
foreach (var item in SubMenu.Items)
{
var child = SubMenu.ItemContainerGenerator.ContainerFromItem(item);
HyperlinkButton hb = child as HyperlinkButton;
// use hb
}
FindVisualChild from Johan Leino answer has bug: traversing of lower levels in control hierarchy does not have any effect because he don't check result of recursive call.
That is fixed version.
public static T FindVisualChild<T>(this DependencyObject instance) where T : DependencyObject
{
T control = default(T);
if (instance != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(instance); i++)
{
if ((control = VisualTreeHelper.GetChild(instance, i) as T) != null)
{
break;
}
if ((control = FindVisualChild<T>(VisualTreeHelper.GetChild(instance, i))) != null)
{
break;
}
}
}
return control;
}
Try this:
foreach (UIElement child in SubMenu.Items.OfType<UIElement>())
This is using the Enumerable.OfType<TResult> extension method that filters the collection down to only those items that are of the specified type.

Silverlight: retrieving controls nested within a Listbox

i made a listbox that generates dynamic controls such as dropdowns & datepicker. i wanted to retrieve the data within the rows. Normally, in windows forms we commonly index the ...Items[i].FindControl("ControlID") method. How do you do about in XAML?
I need to retrieve the changes upon clicking a button.
btw, here's a simple view of my xaml:
<ListBox>
<stackpanel>
<TextBlock />
<stackpanel>
<grid>
<combobox />
<combobox/>
<datepicker />
</grid>
</stackpanel>
</stackpanel>
</ListBox>
Thank you so much!
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
FrameworkElement selectedItem = (sender as ListBox).SelectedItem as FrameworkElement;
List<FrameworkElement> children = new List<FrameworkElement>();
children = GetChildren(selectedItem, ref children);
}
private List<FrameworkElement> GetChildren(FrameworkElement element, ref List<FrameworkElement> list)
{
int count = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < count; i++)
{
FrameworkElement child = VisualTreeHelper.GetChild(element, i) as FrameworkElement;
if(child != null)
{
list.Add(child);
GetChildren(child, ref list);
}
}
return list;
}
This returns all the FrameworkElements (including paths, borders etc). You can easily extend it and call the GetChildren method recursively only if the child is of certain type (ComboBox, StackPanel etc)
I have a helper class with the following two methods to assist with this sort of task.
XAML:
<ListBox Height="236" HorizontalAlignment="Left" Margin="31,23,0,0"
Name="listBox1" VerticalAlignment="Top" Width="245">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Name="sp">
<TextBlock Name="id">id</TextBlock>
<TextBox Name="test" Text="{Binding Key}"></TextBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Using with a List box, you could pass in the selected item:
var v1 =(ListBoxItem) listBox1.ItemContainerGenerator.ContainerFromIndex(
listBox1.SelectedIndex);
TextBox tb = GetChildByName<TextBox>(v1, "test");
tb.Text = "changed";
and you would get the correct textbox for that selected list box item. You can then use that reference to change properties on it.
public T GetChildByName<T>(DependencyObject parent, string name)
where T : class
{
T obj = RecGetChildByName<T>(parent, name) as T;
if (obj == null) throw new Exception("could find control "
+ "of name as child");
return obj;
}
private DependencyObject RecGetChildByName<T>(DependencyObject parent,
string name)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
Control childControl = child as Control;
if (childControl != null)
{
if (childControl.Name == name) return child;
}
if (VisualTreeHelper.GetChildrenCount(child) > 0)
return RecGetChildByName<T>(child, name);
}
return null;
}
The most straightforward way would be to set a two-way binding on of your controls to objects and then the objects will tell you what the values were set to.
Also you can go through your tree by going through the Content properties of the objects until you get to the leaf objects.
Alternatively, you can use the Selected item and call the VisualTreeHelper's GetChild Method until you're at the leaf objects.

Resources