I have a data bound XAML control like this:
<UserControl>
<TextBlock Text="{Binding Text}" />
</UserControl>
and want it to render as a 'drawing' like this:
<UserControl>
<TextBlock Text="Actual text value" />
</UserControl>
Does anyone know how to extract the drawing from an arbitrary control?
UPDATE:
This question does not seem to be clear. So I try to explain a bit more.
The input is a XAML control with databinding. Now I want to convert this to plain XAML without any databinding. The output can be written to disk and displayed by any application which understands XAML, without binding to anything.
So the solution should look something like this:
FrameworkElementinput = (FrameworkElement)XamlReader.Read(inputFile);
input.DataContext = dataObject;
FrameworkElement output = ConvertToNative(input);
XamlWriter.Write(outputFile, output);
I'm looking for an implementation of 'ConvertToNative'
If you're gonna do this for a UserControls, make sure to remove the x:Class attribute first since you'll get a XamlParseException otherwise.
You can load the Xaml file with XamlReader.Load and when you save it with XamlWriter.Save, the Bindings are translated to their actual value. There seems to be some trouble getting the Bindings to update though so I worked around this by subscribing to the Loaded event and add it to a container in the UI, and in the event handler remove if from the container and then save it. This can probably be worked around in a better way though..
private void SomeMethod()
{
CreateXamlWithBindingValues("UserControl1.xaml", "UserControl1_Saved.xaml");
}
private void CreateXamlWithBindingValues(string sourcePath, string savePath)
{
StreamReader streamReader = new StreamReader(sourcePath);
StringReader stringReader = new StringReader(streamReader.ReadToEnd());
XmlReader xmlReader = XmlReader.Create(stringReader);
FrameworkElement loadedObject = (FrameworkElement)XamlReader.Load(xmlReader);
loadedObject.DataContext = UserControlViewModel;
RoutedEventHandler routedEventHandler = null;
routedEventHandler = new RoutedEventHandler(delegate
{
loadedObject.Loaded -= routedEventHandler;
grid1.Children.Remove(loadedObject);
string savedObject = XamlWriter.Save(loadedObject);
StreamWriter streamWriter = new StreamWriter(savePath);
streamWriter.Write(savedObject);
streamWriter.Close();
});
loadedObject.Loaded += routedEventHandler;
grid1.Children.Add(loadedObject);
}
Xaml before Save
<UserControl 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"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding Text}"/>
</Grid>
</UserControl>
Xaml after Save As you can see, there's no linebreaks but the Text value is the value produced from the binding
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"><Grid><TextBlock Text="Actual Text Value" /></Grid></UserControl>
Xaml before Save
<Grid Name="grid1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Button Content="{Binding MyContent}"/>
</Grid>
Xaml after Save
<Grid Name="grid1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"><Button>Actual Content</Button></Grid>
Related
Following btnTest_Click(...) event in my WPF .NET5 app successfully displays the content of a RichTextBox into a FlowDocumentReader. But, as shown in the images below, the different page viewing modes of the FlowDocumentReader create excessive amounts of whitespace on lines. Question: Why it is happening, what I may be missing here, and how can we resolve the issue?
MainWindow.xaml
<Window x:Class="Wpf_RTBFlowDocTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_RTBFlowDocTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<DockPanel Name="mainPanel">
<ToolBar Name="mainToolBar" Height="30" DockPanel.Dock="Top">
<Button x:Name="btnTest" Content="Test" Click="btnTest_Click"/>
</ToolBar>
<RichTextBox Name="rtbTest" AcceptsTab="True" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"/>
<FlowDocumentReader x:Name="fdReader" ScrollViewer.VerticalScrollBarVisibility="Auto" IsScrollViewEnabled="True">
<FlowDocument IsOptimalParagraphEnabled="True" IsHyphenationEnabled="True" TextAlignment="Left"></FlowDocument>
</FlowDocumentReader>
</DockPanel>
</Grid>
</Window>
MainWindow.xaml.cs
private void btnTest_Click(object sender, RoutedEventArgs e)
{
var range = new TextRange(rtbTest.Document.ContentStart, rtbTest.Document.ContentEnd);
if (!range.IsEmpty)
{
if(fdReader.Document.Blocks.Count > 0)
fdReader.Document.Blocks.Clear();
using (var stream = new MemoryStream())
{
range.Save(stream, DataFormats.XamlPackage);
var copyto = new TextRange(fdReader.Document.ContentEnd, fdReader.Document.ContentEnd);
copyto.Load(stream, DataFormats.XamlPackage);
}
}
rtbTest.Visibility = Visibility.Collapsed;
fdReader.Visibility = Visibility.Visible;
}
Original display of the app before clicking the Test button:
Single pageview display after the above code ran:
Single Scroll pageview display after above code ran:
Multiple pageview display after the above code ran:
That's exactly as expected: your source text file is including lots of NewLine characters for line feed inside the sentences and spaces used for lines content alignment:
In a flow document, the content adapts itself to fit the container, but NewLine characters inside sentences prevented the FlowDocument control to format the text correctly.
Therefore, it's necessary to make some source text processing before loading it to the FlowDocument control.
I'm building a WPF app with custom UserControls, and I'm trying to understand how property bindings are supposed to work. I can't get even the most basic binding to work, and it's simple enough to distill into a tiny example, so I figured someone with more WPF experience might be able to put me on the right track.
I've defined a custom UserControl called TestControl, which exposes a Foo property, which is intended to be set in XAML whenever a UserControl is placed.
TestControl.xaml.cs
using System.Windows;
using System.Windows.Controls;
namespace BindingTest
{
public partial class TestControl : UserControl
{
public static readonly DependencyProperty FooProperty = DependencyProperty.Register("Foo", typeof(string), typeof(TestControl));
public string Foo
{
get { return (string)GetValue(FooProperty); }
set { SetValue(FooProperty, value); }
}
public TestControl()
{
InitializeComponent();
}
}
}
The markup for TestControl just defines it as a control with a single button, whose label text displays the current value of the Foo property:
TestControl.xaml
<UserControl x:Class="BindingTest.TestControl"
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:BindingTest"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Button Content="{Binding Foo}" />
</Grid>
</UserControl>
In my MainWindow class, I just place a single instance of TestControl with its Foo property set to "Hello".
MainWindow.xaml
<Window x:Class="BindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BindingTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:TestControl Foo="Hello" />
</Grid>
</Window>
I would expect that when I build and launch this app, I'd see a window with a single button reading "Hello". However, the button is blank: the Binding doesn't seem to work.
If I add a click handler to the TestControl's button, I can verify that the value is being updated behind the scenes:
// Added to TestControl.xaml.cs:
private void Button_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("Button clicked; Foo is '{0}'", Foo);
}
// Updated in TestControl.xaml:
// <Button Content="{Binding Foo}" Click="Button_Click" />
When I click the button, I get Button clicked; Foo is 'Hello', but the GUI never updates. I've tried using Path=Foo, XPath=Foo, etc., as well as setting UpdateSourceTrigger=PropertyChanged and verifying updates with NotifyOnTargetUpdated=True... nothing seems to result in the text in the UI being updated to match the underlying property value, even though the property value seems to be getting updated just fine.
What am I doing wrong? I feel like there's just a simple and fundamental misunderstanding in how I'm approaching this.
edit:
Poking around a bit more and reading similar questions has led me to a potential fix: namely, adding a name to the root UserControl element in TestControl.xaml (x:Name="control"), and changing the binding to explicitly specify that control ({Binding Foo, ElementName=control}).
I'm guessing that by default, {Binding Foo} on the Button element just means "find a property named 'Foo' on this Button control", whereas I'd assumed it'd mean "find a property named 'Foo' in the context that this Button is being declared in, i.e. on the TestControl".
Is specifying an explicit ElementName the best fix here?
You have to set the source object of the Binding to the UserControl instance, e.g. like this:
<Button Content="{Binding Foo, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
or
<UserControl ... x:Name="theControl">
...
<Button Content="{Binding Foo, ElementName=theControl}"/>
If you have many such Bindings, you may also set the DataContext of the top level element in the UserControl's XAML to the UserControl instance:
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<Button Content="{Binding Foo}" />
<Button Content="{Binding Bar}" />
</Grid>
You must however avoid to set the DataContext of the UserControl (which is often recommend by "expert" bloggers), because that would break DataContext-based Bindings of the UserControl properties like
<local:TestControl Foo="{Binding SomeFoo}" />
I am Trying to use the new RadSyntaxEditor from Telerik by following this guide.
This is the code I created:
private RadSyntaxEditor _syntaxEditor;
public RadSyntaxEditor SyntaxEditor
{
get => _syntaxEditor;
set
{
if (Equals(value, _syntaxEditor)) return;
_syntaxEditor = value;
OnPropertyChanged();
}
}
public CodeEditorViewModel()
{
SyntaxEditor = new RadSyntaxEditor();
}
public void Test()
{
using (StreamReader reader = new StreamReader("../../ViewModels/ShellViewModel.cs", Encoding.UTF8))
{
SyntaxEditor.Document = new TextDocument(reader);
}
var cSharpTagger = new CSharpTagger(SyntaxEditor);
SyntaxEditor.TaggersRegistry.RegisterTagger(cSharpTagger);
}
my xaml file:
<UserControl x:Class="CodeEditorControl.Views.CodeEditorView"
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:telerik="http://schemas.telerik.com/2008/xaml/presentation"
mc:Ignorable="d" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button x:Name="Test" Grid.Row="0">Test</Button>
<telerik:RadSyntaxEditor x:Name="SyntaxEditor" Grid.Row="1"/>
</Grid>
</UserControl>
The control is showing without a problem and is responding to input etc.
But neihter does the document load, nor is there any syntax highlighting.
The Reader loads correct and ReadToEnd() outputs the correct text (ShellViewModel is just a standard cs file with 36 lines).
I am using caliburn.micro and the MVVM design.
Edit: I set up a project with the same template but using code behind instead of binding. This works as intended. So the problem lies within the binding from caliburn.micro and telerik.
Any help is appreciated.
I've noticed that the property in CodeEditorViewModel is of type RadSyntaxEditor and the corresponding UI element is also RadSytanxEditor. Note that this produces a binding error in the Output pane of Visual Studio. I think that the Caliburn.Micro binding engine cannot create this type of relation and currently there are two separate instances of RadSyntaxEditor. The one defined in XAML and the other one defined in the view model. The document is loaded to the one defined in code, but because it is never used in the UI, there is nothing in the application.
To resolve this you can research the Caliburn.Micro framework and more specifically, how to use the naming conventions to data bind the model property to a corresponding property of the UI element. I think the current binding (via the convention) defaults to the Visibility property of RadSyntaxEditor.
Or you can simply use an explicit data binding like this:
<Button x:Name="Test" Grid.Row="0">Test</Button>
<ContentControl Content="{Binding SyntaxEditor}" Grid.Row="1"/>
Note that I've replaced the RadSyntaxEditor control with a ContentControl.
I have a simple window:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:self="clr-namespace:WpfApplication1"
Title="MainWindow" Height="435" Width="613">
<StackPanel>
<Canvas Name="canvas">
<self:Red />
</Canvas>
<UserControl Name="uc">
<self:Blue />
</UserControl>
</StackPanel>
</Window>
Redand Blueare very simple UserControls:
<UserControl x:Class="WpfApplication1.Blue"
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"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Rectangle Fill="Blue" Width="100" Height="100" />
</Grid>
</UserControl>
I have created some ContextMenus:
public MainWindow()
{
InitializeComponent();
canvas.ContextMenu = new ContextMenu();
canvas.ContextMenuOpening += (sender, e) =>
{
System.Diagnostics.Debug.WriteLine(e.Source.GetType());
};
uc.ContextMenu = new ContextMenu();
uc.ContextMenuOpening += (sender, e) =>
{
System.Diagnostics.Debug.WriteLine(e.Source.GetType());
};
}
If I open the context menu on the Canvas, the Source is Red, but if I open it on the UserControl, Source is UserControl.
Any idea why?
I found this on MSDN:
ContextMenu itself is a FrameworkElement derived class, but this event will not be raised from the context menu being opened as a source. The event is raised from the element that "owns" the context menu as a property...
If I understand it correctly Source should be Canvas in the first case, but it isn't.
This behavior is covered fairly well in the MSDN documentation for the RoutedEventArgs.OriginalSource property:
Source adjustment by various elements and content models varies from class to class. Each class that adjusts event sources attempts to anticipate which source is the most useful to report for most input scenarios and the scenarios for which the class is intended, and then sets that source as the Source. If this source is not the one that has relevance to your handling of the event, try checking OriginalSource instead to see if it reports a different source that is more suitable.
Which is exactly what the UserControl class does, it patches the Source property in its AdjustBranchSource() method.
So, as hinted by the quoted text, you are perhaps looking for the OriginalSource property to make the code behave similarly, you'll get a reference to the Rectangle in both cases.
I have text boxes that are getting URL inside, when you put the URL (long) in it, I want it to go down one row in order to see the last character of the URL.How can I achieve it instead
of changing the width size?
The TextBlock class features the TextBlock.TextTrimming Property, which enables users to add an ellipsis (...) at the end of text that is too long to be displayed in the TextBlock. If your TextBox is not being used for text input, then you can simply use a TextBlock control instead.
If you really need to use a TextBox, then unfortunately that has no such property. One alternative is to use a custom TextBox that does have this property. You can find an example of that in the WPF TextBox With Ellipsis page on CodeProject.
UPDATE >>>
As you have not shown any code, nobody can tell you what you did wrong. Either way, this is a simple issue that I'm sure that you can fix yourself. Add this to a different view somewhere else:
<TextBlock Text="123456789012345678901234567890123456789012345678901234567890"
Width="100" TextTrimming="WordEllipsis" />
Now you should be able to see the ellipsis at the end of the TextBlock. That's how simple it is. If you example is not working, then you have made it not work by adding something else.
Try scrolling the text box to the beginning of the text when focus lost (not sure how to do that with data binding):
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
(sender as TextBox).ScrollToHome();
}
You can also create a Behavior to avoid direct event handling:
Add reference to System.Windows.Interactivity (installed with Expression Blend).
Add a Behavior class:
using System.Windows.Controls;
using System.Windows.Interactivity;
namespace WpfApplication2
{
public class AutoScrollToHomeBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
AssociatedObject.LostFocus += (tb, args) =>
{
(tb as TextBox).ScrollToHome();
};
}
}
}
Attach a Behavior to your text box:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:e="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel Orientation="Vertical">
<TextBox HorizontalAlignment="Left" Height="23" Width="120">
<e:Interaction.Behaviors>
<local:AutoScrollToHomeBehavior />
</e:Interaction.Behaviors>
</TextBox>
<TextBox HorizontalAlignment="Left" Height="23" Width="120">
<e:Interaction.Behaviors>
<local:AutoScrollToHomeBehavior />
</e:Interaction.Behaviors>
</TextBox>
</StackPanel>
</Grid>
</Window>