Adding programmatically an inline defined in XAML - wpf

I have a FlowDocument. Is it possible to add an inline into it but the inline is defined in XAML. E.g.
<FlowDocument >
<Paragraph Name="Output"/>
</FlowDocument>
var AppendTo = new Span();
var XAML = "<Run>Something</Run>";
AppendTo.Inlines.Add(XAML); // This is what I mean - adding elements using XAML
Output.Inlines.Add(AppendTo);
Thank you
Note - to those who vote to close it because it's too broad. Maybe read the answer that I marked as answer and then think where the problem is.

You can create an actual Run element, or any other kind of element, from a XAML string programmatically using the XamlReader.Parse method:
var AppendTo = new Span();
var pc = new System.Windows.Markup.ParserContext();
pc.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
var run= System.Windows.Markup.XamlReader.Parse("<Run>Something</Run>", pc) as Run;
AppendTo.Inlines.Add(run);
Output.Inlines.Add(AppendTo);

Of course it's possible. It's easier when you assign a name to all relevant elements.
FlowDocument is defined as a resource of a Control
<Window.Resources>
<FlowDocument x:Key="Document">
<Paragraph Name="Inline" />
</FlowDocument>
</Window.Resources>
In the code-behind file of the Control you can use the FrameworkElement.TryFindResource(object) : object or FrameworkElement.FindResource(object) : object to sear through the control's resource:
private void OnLoaded(object sender, RoutedEventArgs e)
{
if (TryFindResource("Document") is FlowDocument document)
{
foreach (Block documentBlock in document.Blocks)
{
if (documentBlock is Paragraph paragraph || documentBlock.Name.Equals("Inline", StringComparison.OrdinalIgnoreCase))
{
// Do something with the Paragraph
}
}
}
}
FlowDocument is defined as element of the Control's visual tree
<Window>
<Grid SnapsToDevicePixels="True">
<RichTextBox>
<RichTextBox.Document>
<FlowDocument x:Name="Document">
<Paragraph Name="Inline" />
</FlowDocument>
</RichTextBox.Document>
</RichTextBox>
</Grid>
</Window>
In code-behind just access the elements by its name. Every element that has a Name value assigned is accessible via the auto-generated field reference:
private void OnLoaded(object sender, RoutedEventArgs e)
{
Paragraph paragraph = this.Inline;
// Do something with the Paragraph
}
The VisualTreeHelper can also be used to traverse the viaual tree and search for the document or inline element,

Related

How can I access control of parent window from child page in WPF?

I have a window "w" with several controls and a frame "f". I use pages "p1, p2, .." to replace "f". I want to access controls of "w" from "p1". How can I do that?
"w.xaml":
<Window x:Class="WpfApplication.w">
<Grid>
<TextBox x:Name="textBox_1" />
<Frame x:Name="mainFrame" />
</Grid>
</window>
"p1.xaml":
<Page x:Class="WpfApplication.p1">
<Grid>
<Button x:Name="button_1"
Click="Button_Click" />
</Grid>
"p1.xaml.cs":
private void Button_Click_Upload(object sender, RoutedEventArgs e)
{
//set text of textBox_1
}
You can do
private void Button_Click_Upload(object sender, RoutedEventArgs e)
{
((w)App.Current.MainWindow).textBox_1.Text = "Your Text";
//or
((w)Window.GetWindow(this)).textBox_1.Text = "Your Text";
}
If you're not against tight coupling, just link "w" with "p1" by means of a property or constructor parameter.You might want to add an interface 'Iw' to loose your coupling a bit.
Another option is to add dependency properties for w's children you need to access in p1 and then bind them using relative binding.
P.S.
It'd be much easier to understand what you're after if you'd shown some code.

Why doesn't this Binding work

I have a 3rd party SplitButton control that exposes some DropDownContent and a boolean IsOpen dp to control whether the drop down content is shown or not.
In the case the DropDownContent is a StackPanel with several Buttons, each of which is bound to a command in the view model. In addition to executing that command, clicking the button needs to close the open DropDown content, which I am doing with the AttachedBehavior below.
But my binding, which simple needs to get a reference to the ancestor SplitButton control doesn't work. In the binding, you will note I am trying to Find the first Ancestor control of type SplitButton. I do see however that the debug info says ancestor level 1, so I changed the level to as high as 4, but still with an error.
Can someone see what the fix is?
binding error
System.Windows.Data Error: 4 : Cannot find source for binding with reference
'RelativeSource FindAncestor, AncestorType='Xceed.Wpf.Toolkit.SplitButton',
AncestorLevel='1''. BindingExpression:(no path); DataItem=null; target element is
'CloseDropDownContentBehavior' (HashCode=8896066); target property is 'DropDownButtonElement' (type 'SplitButton')
xaml
<DataTemplate x:Key="AddNewPartyTemplate">
<StackPanel HorizontalAlignment="Right" Margin="10">
<toolkit:SplitButton x:Name="theSplitButton" Content="{resx:Resx Subject_AddNewWithChoices}">
<toolkit:SplitButton.DropDownContent>
<StackPanel x:Name="theStackPanel">
<Button Content="{resx:Resx Person}" Command="{Binding AddNewPersonCommand}"
>
<i:Interaction.Behaviors>
<local:CloseDropDownContentBehavior
*** DropDownButtonElement="{Binding
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type toolkit:SplitButton}}}"/>
</i:Interaction.Behaviors>
</Button>
...
</StackPanel>
</toolkit:SplitButton.DropDownContent>
</toolkit:SplitButton>
</StackPanel>
</DataTemplate>
attached behavior
public class CloseDropDownContentBehavior : Behavior<ButtonBase>
{
private ButtonBase _button;
protected override void OnAttached()
{
_button = AssociatedObject;
_button.Click += OnPartyButtonClick;
}
protected override void OnDetaching()
{
_button.Click -= OnPartyButtonClick;
}
// **** the point of it all
void OnPartyButtonClick(object sender, RoutedEventArgs e) { DropDownButtonElement.IsOpen = false; }
public static readonly DependencyProperty DropDownButtonElementProperty =
DependencyProperty.Register("DropDownButtonElement",
typeof(SplitButton), typeof(CloseDropDownContentBehavior), new UIPropertyMetadata(null, OnDropDownElementChanged));
public DropDownButton DropDownButtonElement
{
get { return (DropDownButton)GetValue(DropDownButtonElementProperty); }
set { SetValue(DropDownButtonElementProperty, value); }
}
private static void OnDropDownElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
}
}
Guessing it's because Interaction.Behaviors isn't part of the visual tree, so the binding won't find the ancestor. Have you tried simply:
DropDownElement="{Binding ElementName=theSplitButton}"
Update from comments: the solution in this case is to simply use x:Reference:
DropDownElement="{x:Reference theSplitButton}"
i dont know the SplitButton.DropDownContent but if its behave like a context menu the following answer might help: WPF context menu whose items are defined as data templates
this trick is to bind with RelativeSource Self or Type ContextMenu and then set the Path to PlacementTarget.DataContext.YourProperty

Frame navigation - DataContext not inherited

I have a XAML file representing the main window of a WPF application.
Now I want this window to display content that is specified by another XAML file.
This works, but the DataContext is lost in the C# code of my UserControl.
I think the <Frame Source=....> is breaking the logical tree of WPF in some way.
I'd like to have the same behavior as if <Frame Source=....> was simply substituted by the Content1.xaml file content, i.e. that the DataContext of the surrounding Window class is inherited to the UserControl.
Is there a simple way to overcome this issue?
All solutions that I found seem like overkill.
Pseudocode
MainWindow.xaml
<Window ....>
<Frame Source="Content1.xaml" />
</Window>
Content1.xaml
<UserControl ....>
<!-- Content goes here -->
</UserControl>
Joe White's solution here solves the problem.
Quoting from his answer:
In XAML:
<Frame Name="frame"
LoadCompleted="frame_LoadCompleted"
DataContextChanged="frame_DataContextChanged"/>
In codebehind:
private void frame_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
UpdateFrameDataContext(sender, e);
}
private void frame_LoadCompleted(object sender, NavigationEventArgs e)
{
UpdateFrameDataContext(sender, e);
}
private void UpdateFrameDataContext(object sender, NavigationEventArgs e)
{
var content = frame.Content as FrameworkElement;
if (content == null)
return;
content.DataContext = frame.DataContext;
}

Is there a way to call external functions from xaml?

Is there any way to call methods of external objects (for example resource objects) directly from xaml?
I mean something like this:
<Grid xmlns:dm="clr-namespace:MyNameSpace;assembly=MyAssembly">
<Grid.Resources>
<dm:TimeSource x:Key="timesource1"/>
</Grid.Resources>
<Button Click="timesource_updade">Update time</Button>
</Grid>
The method timesource_update is of course the method of the TimeSource object.
I need to use pure XAML, not any code behind.
Check this thread, it has a similar problem. In general you can't call a method directly from xaml.
You could use Commands or you can create an object from xaml which will create a method on a thread, which will dispose itself when it needs.
But I am afraid you can't do it just in pure XAML. In C# you can do everything you can do in XAML, but not other way round. You can only do some certain things from XAML that you can do in C#.
OK, here is the final sollution.
XAML:
<Grid xmlns:dm="clr-namespace:MyNameSpace;assembly=MyAssembly">
<Grid.Resources>
<dm:TimeSource x:Key="timesource1"/>
</Grid.Resources>
<Button Command="{x:Static dm:TimeSource.Update}"
CommandParameter="any_parameter"
CommandTarget="{Binding Source={StaticResource timesource1}}">Update time</Button>
</Grid>
CODE in the TimeSource class:
public class TimeSource : System.Windows.UIElement {
public static RoutedCommand Update = new RoutedCommand();
private void UpdateExecuted(object sender, ExecutedRoutedEventArgs e)
{
// code
}
private void UpdateCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
// Constructor
public TimeSource() {
CommandBinding cb = new CommandBinding(TimeSource.Update, UpdateExecuted, UpdateCanExecute);
CommandBindings.Add(cb2);
}
}
TimeSource has to be derived from UIElement in order to have CommandBindings. But the result is calling outer assembly method directly from XAML. By clicking the button, 'UpdateExecuted' method of the object timesource1 is called and that is exactly what I was looking for.

Set the focus on a textbox in xaml wpf

Despite some posts on this forum and others i cannot find something that tells me how to set the focus on a TextBox.
I have a userControl with many labels and textBoxes. When the form is loaded I want the a particular textBox to have the focus.
I have set the tabIndex but that didn't seem to work.
Any suggestions?
You can use the FocusManager.FocusedElement attached property for this purpose. Here's a piece of code that set the focus to TxtB by default.
<StackPanel Orientation="Vertical" FocusManager.FocusedElement="{Binding ElementName=TxtB}">
<TextBox x:Name="TxtA" Text="A" />
<TextBox x:Name="TxtB" Text="B" />
</StackPanel>
You can also use TxtB.Focus() in your code-behind if you don't want to do this in XAML.
You can apply this property directly on the TextBox :
<TextBox Text="{Binding MyText}" FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"/>
I am new to using WPF and reading through the above examples I had a similar experience trying set the focus to a textbox using the xaml code examples given, i.e. all the examples above didn't work.
What I found was I had to place the FocusManager.FocusElement in the page element. I assume this would probably work as well if you used a Window as the parent element. Anyway, here is the code that worked for me.
<Page x:Class="NameOfYourClass"
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"
mc:Ignorable="d"
Title="Title"
Height="720"
Width="915"
Background="white"
Loaded="pgLoaded"
FocusManager.FocusedElement="{Binding ElementName=NameOfYourTextBox}">
<!-- Create child elements here. -->
</Page>
I have a TextBox inside a Grid inside a DataTemplate which I want to have keyboard focus when it becomes visible. I also found that
<DataTemplate x:Key="DistanceView" DataType="{x:Type vm:ROI}">
<Grid FocusManager.FocusedElement="{Binding ElementName=tbDistance}">
<TextBox x:Name="tbDistance" Grid.Column="1" Grid.Row="1" VerticalAlignment="Bottom"/>
</Grid>
</DataTemplate>
did not work for me.
However when I call Focus() in the parent ContentControl
private void ContentControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((sender as ContentControl).IsVisible)
{
(sender as ContentControl).Focus();
}
}
it starts to work and the caret is visible in the TextBox. I think the FocusScope has to be given focus for the FocusManager.FocusedElement property to have any effect.
Jerry
From experimenting around, the xaml solution
FocusManager.FocusedElement="{Binding ElementName=yourElement}"
seems to work best when you place it in the highest element in the window hierarchy (usually Window, or the Grid you place everything else in)
Usage:
local:FocusManager.FocusOnLoad="True"
public class FocusManager
{
public static readonly DependencyProperty FocusOnLoad = DependencyProperty.RegisterAttached(
"FocusOnLoad",
typeof(bool),
typeof(FocusManager),
new UIPropertyMetadata(false, new PropertyChangedCallback(OnValueChanged))
);
private static void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (!(sender is Control control))
return;
if ((bool) e.NewValue == false)
return;
control.Loaded += (s, e) => control.Focus();
}
public static bool GetFocusOnLoad(DependencyObject d) => (bool) d.GetValue(FocusOnLoad);
public static void SetFocusOnLoad(DependencyObject d, bool value) => d.SetValue(FocusOnLoad, value);
}
FocusManager was not in intellisense and this confused me a bit. I just typed the entire attribute and it worked.
FocusManager.FocusedElement="{Binding ElementName=MyTextBox}"
Microsoft Visual Studio Enterprise 2015 version 14.0.23107.0/C#/WPF
For completeness, there is also a way to handle this from code behind (e.g. in the case of controls that, for whatever reason, are created dynamically and don't exist in XAML). Attach a handler to the window's Loaded event and then use the ".Focus()" method of the control you want. Bare-bones example below.
public class MyWindow
{
private VisualCollection controls;
private TextBox textBox;
// constructor
public MyWindow()
{
controls = new VisualCollection(this);
textBox = new TextBox();
controls.Add(textBox);
Loaded += window_Loaded;
}
private void window_Loaded(object sender, RoutedEventArgs e)
{
textBox.Focus();
}
}
bind the element you want to point the focus in as
FocusManager.FocusedElement= "{Binding ElementName= Comobox1}"
in grid or groupbox etc
Further to my comment on Feb 04 '22, I solved it this way:
In the UserControl definitionin the XAML add a Loaded event handler. (pressing tab after Loaded= will automatically add an event handler to the code behind)
Then edit the event handler in the code behind:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
expressionTextBox.Focus();
}
I'm hoping that WPF is clever enough to handle th unhooking of the evnt at some point, allowing the class to be garbage collected and not give rise to memory leaks, but I don't know. I'd be interested in any comments on that.

Resources