User control with richTextBox, bindable richTextBox - wpf

I try make user control with richTextBox because I need bindable richTextbox.
I found some solution here: Richtextbox wpf binding.
I would like to use solution of Arcturus. Create user control with richTextBox control and use dependency property.
In XAML I have only richTextBox control:
<UserControl x:Class="WpfApplication2.BindableRichTextBoxControl"
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>
<RichTextBox Name="RichTextBox" Grid.Row="0"/>
</Grid>
</UserControl>
In CodeBehind:
public partial class BindableRichTextBoxControl : UserControl
{
public static readonly DependencyProperty DocumentProperty =
DependencyProperty.Register("Document", typeof(FlowDocument), typeof(BindableRichTextBoxControl),
new PropertyMetadata(OnDocumentChanged));
public FlowDocument Document
{
get { return (FlowDocument)GetValue(DocumentProperty); }
set { SetValue(DocumentProperty, value); }
}
private static void OnDocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (BindableRichTextBoxControl)d;
if (e.NewValue == null)
control.RichTextBox.Document=new FlowDocument();
//?
control.RichTextBox.Document = document;
}
public BindableRichTextBoxControl()
{
InitializeComponent();
}
}
I am little confuse with last line in OnDocumentChanged method.
control.RichTextBox.Document = document;
I can’t identify what is varibale document.

I think he means this:
private static void OnDocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
RichTextBoxControl control = (RichTextBoxControl) d;
if (e.NewValue == null)
control.RTB.Document = new FlowDocument(); //Document is not amused by null :)
else
control.RTB.Document = e.NewValue;
}
but I recommend you leave a comment on his original answer.

Related

How do I find what text had been added with TextChanged

I'm looking to synchronize between a text in the textbox and string in a variable. I found how to get the index in which the string was changed (in the textbox), the length added and length removed, but how can I actually find the string added?
So far I've used TextChangedEventArgs.Changes, and got the properties of the items in it (ICollection).
I'm trying to create a password box in which I could show the actual password by a function. hence I do not want the textbox to synchronize directly (for example, in the textbox would appear "*****" and in the string "hello").
If you want only text added you can do this
string AddedText;
private void textbox_TextChanged(object sender, TextChangedEventArgs e)
{
var changes = e.Changes.Last();
if (changes.AddedLength > 0)
{
AddedText = textbox.Text.Substring(changes.Offset,changes.AddedLength);
}
}
Edit
If you want all added and remove text you can do this
string oldText;
private void textbox_GotFocus(object sender, RoutedEventArgs e)
{
oldText = textbox.Text;
}
string AddedText;
string RemovedText;
private void textbox_TextChanged(object sender, TextChangedEventArgs e)
{
var changes = e.Changes.Last();
if (changes.AddedLength > 0)
{
AddedText = textbox.Text.Substring(changes.Offset, changes.AddedLength);
if (changes.RemovedLength == 0)
{
oldText = textbox.Text;
RemovedText = "";
}
}
if (changes.RemovedLength > 0)
{
RemovedText = oldText.Substring(changes.Offset, changes.RemovedLength);
oldText = textbox.Text;
if (changes.AddedLength == 0)
{
AddedText = "";
}
}
}
DataBinding is the most common way in WPF to show and collect data in a UI
Try this:
<Window x:Class="WpfApp3.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:WpfApp3"
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525">
<Grid>
<TextBox Text="{Binding Path=SomeText, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left"
Margin="101,83,0,0"
VerticalAlignment="Top"
Width="75" />
<TextBlock Text="{Binding SomeText}"
HorizontalAlignment="Left"
Margin="101,140,0,0"
VerticalAlignment="Top"
Width="75" />
</Grid>
</Window>
Code for the window:
public partial class MainWindow : Window
{
private readonly AViewModel viewModel = new AViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = viewModel;
}
}
And the code for the ViewModel that holds the data you want to show and collect:
public class AViewModel : INotifyPropertyChanged
{
private string someText;
public string SomeText
{
get
{
return someText;
}
set
{
if (Equals(this.someText, value))
{
return;
}
this.someText = value;
this.OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(
[CallerMemberName]string propertyName = null)
{
this.PropertyChanged?.Invoke(
this,
new PropertyChangedEventArgs(propertyName));
}
}
Although this looks complicated for a simple scenario it has a lot of advantages:
You can write automated (unit)test for the ViewModel without creating a UI
Adding extra fields and logic is trivial
If the UI needs to change, the ViewModel will not always need to change
The core of the mechanism is the {Binding ...} bit in the Xaml that tell WPF to synchronize the data between the Text property of the TextBox and the SomeText property of the object that is assigned to the DataContext.
The other significant bits are:
- in the constructor of the window the setting of the DataContext and
- in the ViewModel the raising of the PropertyChanged event when the SomeText property changes so the binding will be notified.
Note that this is just a basic example of DataBinding, there are many improvements that could be made in this code.

WPF Attached Property triggering twice

I am trying to learn dependency properties and attached properties, so please forgive me if you find no use in what I am trying to do.
I have a usual MVVM approach with a Window whose datacontext is set to a VM, and View which is a datatemplate containing a usercontrol targetting such VM.
I am trying to make the window container as dumb as possible, as such i'm trying to define some parameters that usually reside in the window XAML (e.g. the height) via the usercontrol using Attached Properties.
For that purpose I created the following class where I define the attached property:
public static class WpfExtensions
{
public static readonly DependencyProperty ContainerHeightProperty = DependencyProperty.RegisterAttached(
"ContainerHeight",
typeof(double),
typeof(WpfExtensions),
new FrameworkPropertyMetadata(300.0, FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsParentMeasure | FrameworkPropertyMetadataOptions.AffectsRender, ContainerHeight)
);
public static void SetContainerHeight(UIElement element, double value)
{
element.SetValue(ContainerHeightProperty, value);
}
public static double GetContainerHeight((UIElement element)
{
return (double)element.GetValue(ContainerHeightProperty);
}
private static void ContainerHeight(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is UserControl)
{
UserControl l_Control = (UserControl)d;
Binding l_Binding = new Binding();
l_Binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Window), 1);
l_Binding.Path = new PropertyPath("Height");
l_Binding.Mode = BindingMode.OneWayToSource;
BindingOperations.SetBinding(d, e.Property, l_Binding);
}
}
}
as you can see, to achieve the control of the container window height I am creating a binding in code from the attached property up to the container window.
However the ContainerHeight change get fired twice. The first time I get a change from 300 (the default) to 1024(what is defined in XAML). Then I immediately receive another one from 1024 back to 300 and I am not understanding why.
The window code is very simple:
<Window x:Class="WpfApplication.DialogWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:lcl="clr-namespace:WpfApplication"
Title="DialogWindow">
<Window.Resources>
<DataTemplate DataType="{x:Type lcl:Dialog_VM}">
<lcl:Dialog_VM_View />
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding }" />
</Window>
and finally the simple ViewModel
<UserControl x:Class="WpfApplication.Dialog_VM_View"
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:lcl="clr-namespace:WpfApplication"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
lcl:WpfExtensions.ContainerHeight="1024">
<StackPanel>
<TextBlock Text="This is a test" />
</StackPanel>
</UserControl>
You should bind the Height property of the parent window to the attached property and not the other way around, shouldn't you?
This sets (binds) the Height of the parent window to 1024 which is the value of the dependency property in the UserControl:
private static void ContainerHeight(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is UserControl)
{
UserControl l_Control = (UserControl)d;
if (!l_Control.IsLoaded)
{
l_Control.Loaded += L_Control_Loaded;
}
else
{
Bind(l_Control);
}
}
}
private static void L_Control_Loaded(object sender, RoutedEventArgs e)
{
UserControl l_Control = (UserControl)sender;
Bind(l_Control);
}
private static void Bind(UserControl l_Control)
{
Window window = Window.GetWindow(l_Control);
Binding l_Binding = new Binding();
l_Binding.Path = new PropertyPath(WpfExtensions.ContainerHeightProperty);
l_Binding.Source = l_Control;
BindingOperations.SetBinding(window, Window.HeightProperty, l_Binding);
}

Behaviour of ItemsControl

I have a screen with several UserControls, but only one of them remains active. The other UserControls aren't shown, but the user can switch the active flag of any of those who are not active. One of the UserControl contains an ItemsControl.
I need to know all the controls in the view, including those generated by an ItemsControl, after loading the first UserControl that is active in the screen, when view is finally initialized.
For ItemsControl, wpf didn't instance any item until it was painted on the screen that contains the UserControl (so I've tried, until the Load event is launched), so that I can't found the controls contained by the view because it didn't exist.
Is there any way to change this behavior?
I try to change the value of property VirtualizingStackPanel.IsVirtualizing to false, to avoid the previous behaviour, with no success. To illustrate this, I write this view example:
<Window x:Class="ContenidoEnTabs.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel x:Name="spContainer" Orientation="Vertical" VirtualizingStackPanel.IsVirtualizing="False">
<Button Content="Push" Click="Button_Click" />
</StackPanel>
</Window>
This view creates a second control not visible until the user press the button:
public partial class MainWindow : Window
{
private NotPaintedOnInitUserControl controlExtra;
public MainWindow()
{
InitializeComponent();
controlExtra = new NotPaintedOnInitUserControl();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
spContainer.Children.Add(controlExtra);
}
}
The control not visible initially is as follow:
<UserControl x:Class="ContenidoEnTabs.NotPaintedOnInitUserControl"
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">
<ItemsControl ItemsSource="{Binding MyCollection}" x:Name="itemsControlTarget"
VirtualizingStackPanel.IsVirtualizing="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox x:Name="aTextBox" Width="80" Initialized="ATextBox_Initialized" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
and in CodeBehind I detect when the Items were created
public partial class NotPaintedOnInitUserControl : UserControl
{
public NotPaintedOnInitUserControl()
{
InitializeComponent();
DataContext = new SimpleListDataContext();
}
private void ATextBox_Initialized(object sender, EventArgs e)
{
}
}
And the DataContext used:
public class SimpleListDataContext
{
private List<string> _myCollection;
public List<string> MyCollection
{
get { return _myCollection ?? (_myCollection = new List<string> { "one", "two" }); }
set { _myCollection = value; }
}
}
Any ideas?
Thanks in advance.
If you want WPF to generate the tree for a control that isn't part of the view, you can "hydrate" and layout the control by forcing the layout to run. Something like this should work:
public partial class MainWindow : Window
{
private NotPaintedOnInitUserControl controlExtra;
public MainWindow()
{
InitializeComponent();
controlExtra = new NotPaintedOnInitUserControl();
// Force the control to render, even though it's not on the screen yet.
var size = new Size(this.Width, this.Height);
var rect = new Rect(new Point(0,0), size);
controlExtra.Measure(size);
controlExtra.Arrange(rect);
controlExtra.InvalidateVisual();
controlExtra.UpdateLayout();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
spContainer.Children.Add(controlExtra);
}
}
Not sure if this is what you're asking. If not, please clarify paragraph 2.
Have a look at LogicalTreeHelper.GetChildren(myUiElement)
This looks at the logical tree rather than the visual tree so it examines the structure without needing to have loaded the control to get the visual structure
In the below control to find is the name of the contorl i.e. myDatagrid
You could also adapt this to just get all the children of a particular control i.e.
FindChildInVisualTree(this, "mydatagrid"); // assumming this a UIElement (i.e. your in the code behind)
find the control using the below then using LogicalTreeHelper get all it's children.
public static UIElement FindChildInVisualTree(UIElement view, string controlToFind)
{
UIElement control = null;
try
{
if (view != null)
{
if ((view as FrameworkElement).Name.ToUpper() == controlToFind.ToUpper())
{
control = view;
}
else
{
DependencyObject depObj = view as DependencyObject;
if (depObj != null)
{
foreach (var item in LogicalTreeHelper.GetChildren(depObj))
{
control = FindChildInVisualTree(item as UIElement, controlToFind);
if (control != null)
{
break;
}
}
}
}
}
}
catch (Exception ex)
{
throw new ApplicationException("Error finding child control: " + controlToFind, ex);
}
return control;
}

wpf databound custom control - why are changes to my datasource not detected by the custom control?

I have a custom control and a view model object. A property on the view model is bound to the custom control and I can see that the custom control actually receives the vaule from the view model object - yet my handler code (GeometryText.Set) is not executed. What am I doing wrong?!
Notice the event handlers on custom control where I've placed breakpoints- if I change the size of the window, I can inspect the GeometryText property in the watch window - and it's clearly updated in the cases where I expect it to.
Thanks for any input,
Anders, Denmark
ComponentDrawing.xaml.cs
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using Rap1D.ServiceLayer.Interfaces.Services;
using StructureMap;
namespace Rap1D.Rap1D_WPF.Controls
{
/// <summary>
/// Interaction logic for ComponentDrawing.xaml
/// </summary>
public partial class ComponentDrawing
{
public static DependencyProperty GeometryTextProperty =DependencyProperty.Register("GeometryText", typeof (string), typeof (ComponentDrawing), new FrameworkPropertyMetadata
(
"",
FrameworkPropertyMetadataOptions
.
None));
private Canvas _canvas;
public ComponentDrawing()
{
InitializeComponent();
}
public string GeometryText
{
get { return ((string) GetValue(GeometryTextProperty)); }
set
{
SetValue(GeometryTextProperty, value);
ReadGeometryTextIntoDrawing(value);
}
}
private void ReadGeometryTextIntoDrawing(string fileText)
{
// Allow control to be visible at design time without errors shown.
// I.e. - don't execute code below at design time.
if (DesignerProperties.GetIsInDesignMode(this))
return;
// OK - we are a running application
//if (_canvas != null)
// return;
// OK - this is first time (-ish) we are running
if (ActualWidth == 0)
return;
// We have a valid screen to pain on
var componentDrawingService = ObjectFactory.GetInstance<IComponentDrawingService>();
//var commandTextProvider = ObjectFactory.GetInstance<ICommandTextProvider>();
//var fileText = ((IViewModelBase) DataContext).GeometryText;
// If getting the file text fails for some reason, just abort to avoid further problems.
if (fileText == null)
return;
var pg = componentDrawingService.GetDrawings(fileText, 0, ActualWidth, 0, ActualHeight);
_canvas = new Canvas();
foreach (var path in pg)
{
_canvas.Children.Add(path);
}
Content = _canvas;
}
private void UserControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
//ReadGeometryTextIntoDrawing();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
//ReadGeometryTextIntoDrawing();
}
private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
//ReadGeometryTextIntoDrawing();
}
}
}
ComponentDrawing.xaml:
<UserControl x:Class="Rap1D.Rap1D_WPF.Controls.ComponentDrawing" 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"
DataContextChanged="UserControl_DataContextChanged" Loaded="UserControl_Loaded" SizeChanged="UserControl_SizeChanged">
<Grid Background="White">
<Path Stroke="Black"></Path>
</Grid>
</UserControl>
Usage:
<Controls:RadPane x:Class="Rap1D.Rap1D_WPF.Controls.ProductComponentDetails" 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:Controls="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Docking" xmlns:Controls1="clr-namespace:Rap1D.Rap1D_WPF.Controls" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" Header="{Binding DisplayName}">
<Controls1:ComponentDrawing GeometryText="{Binding GeometryText}" />
</Controls:RadPane>
view model object (implementing INotifyPropertyChanged):
using System;
using Microsoft.Practices.Prism.Events;
using Rap1D.ExternalInterfaceWrappers.Interfaces;
using Rap1D.ModelLayer.Interfaces.Adapters;
using Rap1D.ModelLayer.Interfaces.Structure;
using Rap1D.ServiceLayer.Interfaces.Adapters;
using Rap1D.ServiceLayer.Interfaces.Providers;
using Rap1D.ViewModelLayer.Interfaces;
namespace Rap1D.ViewModelLayer.Implementations
{
public class ProductComponentViewModel : TreeViewItemViewModel, IProductComponentViewModel
{
...
public override string GeometryText
{
get
{
var pentaResponse = _commandTextProvider.GetCommandText(ProductComponent);
return DateTime.Now.ToString()+ pentaResponse.Payload;
}
}
...
}
}
Dependency property setters are not invoked if changed by binding. If you want to somehow react on dependency property value changing you should register a callback in property metadata:
http://msdn.microsoft.com/en-us/library/ms557294.aspx
something like that (not sure it is compilable, let me know if something wrong):
public static DependencyProperty GeometryTextProperty =
DependencyProperty.Register(... , new FrameworkPropertyMetadata(GeometryTextCallback));
public static void GeometryTextCallback(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
// cast source to your type and invoke method from your setter
((ComponentDrawing)source)ReadGeometryTextIntoDrawing(value);
}

WPF Ribbon Control - Change Content of a Ribbon Button

Is there any way to change the Content of a RibbonButton?
I know that a RibbonButton has an Image and a Label but I want a button with a shape (Rectangle class) in it's Content and this button should have the RibbonButton style applied. Is there any way to do that?
Why don't you create your own button control?
XAML:
<Button x:Class="MyApp.myButton"
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="71" d:DesignWidth="87">
<Rectangle Height="40" Width="40" Fill="#FFC11414" />
Code behind:
public partial class myButton : Button, IRibbonControl
{
public myButton()
{
InitializeComponent();
Type forType = typeof(myButton);
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(forType, new FrameworkPropertyMetadata(forType));
ButtonBase.CommandProperty.OverrideMetadata(forType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));
FrameworkElement.ToolTipProperty.OverrideMetadata(forType, new FrameworkPropertyMetadata(null, new CoerceValueCallback(RibbonButton.CoerceToolTip)));
ToolTipService.ShowOnDisabledProperty.OverrideMetadata(forType, new FrameworkPropertyMetadata(true));
}
public static object CoerceToolTip(DependencyObject d, object value)
{
if (value == null)
{
RibbonButton button = (RibbonButton)d;
RibbonCommand command = button.Command as RibbonCommand;
if ((command == null) || ((string.IsNullOrEmpty(command.ToolTipTitle) && string.IsNullOrEmpty(command.ToolTipDescription)) && (command.ToolTipImageSource == null)))
{
return value;
}
value = new RibbonToolTip(command);
}
return value;
}
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((myButton)d).CoerceValue(FrameworkElement.ToolTipProperty);
}
}

Resources