Trying to use the C# SpellCheck class - c#

I am trying to use the SpellCheck class C# provides (in PresentationFramework.dll).
But, I am experiencing problems when trying to bind the spelling to my textbox:
SpellCheck.SetIsEnabled(txtWhatever, true);
The problem is that my txtWhatever is of type System.Windows.Forms and the parameter this function is looking for is System.Windows.Controls, and simple converting failed.
I also tried to make my TextBox of this type, but... couldn't.
Does anyone know how to use this SpellCheck object?
(MSDN wasn't that helpful...)
Thanks

You have to use a WPF TextBox to make spell checking work. You can embed one in a Windows Forms form with the ElementHost control. It works pretty similar to a UserControl. Here's a control that you can drop straight from the toolbox. To get started, you need Project + Add Reference and select WindowsFormsIntegration, System.Design and the WPF assemblies PresentationCore, PresentationFramework and WindowsBase.
Add a new class to your project and paste the code shown below. Compile. Drop the SpellBox control from the top of the toolbox onto a form. It supports the TextChanged event and the Multiline and WordWrap properties. There's a nagging problem with the Font, there is no easy way to map a WF Font to the WPF font properties. The easiest workaround for that is to set the form's Font to "Segoe UI", the default for WPF.
using System;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.Integration;
using System.Windows.Forms.Design;
[Designer(typeof(ControlDesigner))]
//[DesignerSerializer("System.Windows.Forms.Design.ControlCodeDomSerializer, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.ComponentModel.Design.Serialization.CodeDomSerializer, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
class SpellBox : ElementHost {
public SpellBox() {
box = new TextBox();
base.Child = box;
box.TextChanged += (s, e) => OnTextChanged(EventArgs.Empty);
box.SpellCheck.IsEnabled = true;
box.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
this.Size = new System.Drawing.Size(100, 20);
}
public override string Text {
get { return box.Text; }
set { box.Text = value; }
}
[DefaultValue(false)]
public bool Multiline {
get { return box.AcceptsReturn; }
set { box.AcceptsReturn = value; }
}
[DefaultValue(false)]
public bool WordWrap {
get { return box.TextWrapping != TextWrapping.NoWrap; }
set { box.TextWrapping = value ? TextWrapping.Wrap : TextWrapping.NoWrap; }
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new System.Windows.UIElement Child {
get { return base.Child; }
set { /* Do nothing to solve a problem with the serializer !! */ }
}
private TextBox box;
}
By popular demand, a VB.NET version of this code that avoids the lambda:
Imports System
Imports System.ComponentModel
Imports System.ComponentModel.Design.Serialization
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Forms.Integration
Imports System.Windows.Forms.Design
<Designer(GetType(ControlDesigner))> _
Class SpellBox
Inherits ElementHost
Public Sub New()
box = New TextBox()
MyBase.Child = box
AddHandler box.TextChanged, AddressOf box_TextChanged
box.SpellCheck.IsEnabled = True
box.VerticalScrollBarVisibility = ScrollBarVisibility.Auto
Me.Size = New System.Drawing.Size(100, 20)
End Sub
Private Sub box_TextChanged(ByVal sender As Object, ByVal e As EventArgs)
OnTextChanged(EventArgs.Empty)
End Sub
Public Overrides Property Text() As String
Get
Return box.Text
End Get
Set(ByVal value As String)
box.Text = value
End Set
End Property
<DefaultValue(False)> _
Public Property MultiLine() As Boolean
Get
Return box.AcceptsReturn
End Get
Set(ByVal value As Boolean)
box.AcceptsReturn = value
End Set
End Property
<DefaultValue(False)> _
Public Property WordWrap() As Boolean
Get
Return box.TextWrapping <> TextWrapping.NoWrap
End Get
Set(ByVal value As Boolean)
If value Then
box.TextWrapping = TextWrapping.Wrap
Else
box.TextWrapping = TextWrapping.NoWrap
End If
End Set
End Property
<DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
Public Shadows Property Child() As System.Windows.UIElement
Get
Return MyBase.Child
End Get
Set(ByVal value As System.Windows.UIElement)
'' Do nothing to solve a problem with the serializer !!
End Set
End Property
Private box As TextBox
End Class

Have you tried just setting the property on the actual TextBox your attempting to spellcheck. e.g.
txtWhatever.SpellCheck.IsEnabled = true;

You're trying to use a spell-check component designed for WPF on a WinForms application. They're incompatible.
If you want to use the .NET-provided spell check, you'll have to use WPF as your widget system.
If you want to stick with WinForms, you'll need a third-party spell check component.

Free .NET spell checker based around a WPF text box that can be used client or server side can be seen here. It will wrap the text box for you although you still need the assembly includes to Presentation framework etc.
Full disclosure...written by yours truly

I needed to add a background colour to the textbox in winforms that reflected the colour selected in the designer:
public override System.Drawing.Color BackColor
{
get
{
if (box == null) { return Color.White; }
System.Windows.Media.Brush br = box.Background;
byte a = ((System.Windows.Media.SolidColorBrush)(br)).Color.A;
byte g = ((System.Windows.Media.SolidColorBrush)(br)).Color.G;
byte r = ((System.Windows.Media.SolidColorBrush)(br)).Color.R;
byte b = ((System.Windows.Media.SolidColorBrush)(br)).Color.B;
return System.Drawing.Color.FromArgb((int)a, (int)r, (int)g, (int)b);
}
set
{
box.Background = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromArgb(value.A, value.R, value.G, value.B));
}
}

If you want to enable the TextChanged Event write the code like this.
using System;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.Integration;
using System.Windows.Forms.Design;
[Designer(typeof(ControlDesigner))]
//[DesignerSerializer("System.Windows.Forms.Design.ControlCodeDomSerializer, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.ComponentModel.Design.Serialization.CodeDomSerializer, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
class SpellBox : ElementHost
{
public SpellBox()
{
box = new TextBox();
base.Child = box;
box.TextChanged += (s, e) => OnTextChanged(EventArgs.Empty);
box.SpellCheck.IsEnabled = true;
box.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
box.TextChanged += new System.Windows.Controls.TextChangedEventHandler(SpellBox_TextChanged);
this.Size = new System.Drawing.Size(100, 20);
}
[Browsable(true)]
[Category("Action")]
[Description("Invoked when Text Changes")]
public new event EventHandler TextChanged;
protected void SpellBox_TextChanged(object sender, EventArgs e)
{
if (this.TextChanged!=null)
this.TextChanged(this, e);
}
public override string Text
{
get { return box.Text; }
set { box.Text = value; }
}
[DefaultValue(false)]
public bool Multiline
{
get { return box.AcceptsReturn; }
set { box.AcceptsReturn = value; }
}
[DefaultValue(false)]
public bool WordWrap
{
get { return box.TextWrapping != TextWrapping.NoWrap; }
set { box.TextWrapping = value ? TextWrapping.Wrap : TextWrapping.NoWrap; }
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new System.Windows.UIElement Child
{
get { return base.Child; }
set { /* Do nothing to solve a problem with the serializer !! */ }
}
private TextBox box;
}

what about getting a list of words in the english language and copying that to a text file. add the reference. then use streamreader class to analyze the list against textbox.text. any words not found in the text file could be set to be highlighted or displayed in a dialog box with options to replace or ignore. this is a shotgun suggestion with many missing steps and i am 2 months into programming but....its what im going to attempt anyway. i am making a notepad project (rexpad on idreamincode.com). hope this helped!

Related

Not able to set "SelectedValue" in Combobox bound with enum

I want to set default value (rather than first one) in combobox through SelectedItem/SelectedText/SelectedValue (from any one way).
I have tried to resolve it through many different ways. Like I have set enum to Key Value Pair. Tried to use "SelectedIndex". It is showing '-1' while debugging. In other "selected*" options, value is 'null'. I do not know what is happening. I have attached code. Please take a look and help me to resolve it. Thank you.
ComboBox cmbxType= new ComboBox();
cmbxType.FormattingEnabled = true;
cmbxType.DropDownStyle = ComboBoxStyle.DropDownList;
cmbxType.Margin = new Padding(3, 6, 3, 3);
cmbxType.Name = "cmbxType";
cmbxType.Size = new System.Drawing.Size(200, 28);
cmbxType.TabIndex = 1;
cmbxType.DataSource = Enum.GetValues(typeof(StateType));
cmbxType.SelectedIndexChanged += new System.EventHandler(cmbxType_SelectedIndexChanged);
cmbxType.ValueMember = (workflowRows).ToString();
cmbxType.SelectedValue = 2
PS: I am creating this combobox after creating form and the problematic case is with enum only.
According to this Stack Overflow question you can only set the selected item after the data bindings are assigned. If you select the item when the OnLoad event occures it should work. Below is a working example.
using System;
using System.Windows.Forms;
namespace WindowsFormsApp
{
static class Program
{
internal enum StateType
{
State1,
State2,
State3
}
internal class DemoForm : Form
{
ComboBox cmbxType = new ComboBox();
public DemoForm()
{
cmbxType.DataSource = Enum.GetValues(typeof(StateType));
Controls.Add(cmbxType);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
cmbxType.SelectedItem = StateType.State3;
}
}
[STAThread]
static void Main()
{
Application.Run(new DemoForm());
}
}
}

Can I use Tag property to store it is name?

I am using below method to validate data within textbox in groupbox
so for improvement of user Feedback for example
message text Please enter First Name
where First Name is the label for textbox, so I used Textbox.Tag to store the name of textbox to achieve it since there is no link between the Textbox and it is Label
I search and found that I can use the Tag property to store anything but I wan to be sure of using it by the I told you about
Is there any problem with that ?
public int ValidateData()
{
foreach (Control cont in GB_PatientInfo.Controls)
{
if (cont is TextBox)
{
if (string.IsNullOrWhiteSpace(cont.Text.Trim()))
{
MessageBox.Show("enter data " + cont.Tag, "Message", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, MessageBoxOptions.RtlReading);
cont.BackColor = Color.Red;
cont.Focus();
return -1;
}
}
}
return 1;
}
Thanks
As you have read, you can store any type that derives from object (i.e. everything) in the Control.Tag property so storing the name of a label is fine.
The use of the Tag property does not influence your application. You can store whatever you want in there with no problem.
As it's already mentioned in other answers, it's OK to use Tag property to store any kind of additional information about the control, including a display name.
But have you ever noticed how ToolTip lets you to set ToolTip for your control at design-time?
A ToolTip, ErrorProvider or HelpProvider are examples of extender provider components. They add some properties to the controls at design-time. You also can create such component for DisplayName by implementing IExtenderProvider.
Example
The following code shows you how easily you can create a component called DisplayNameExtender. When you drop an instance of this component on the form, then a new property will be added to design-time of controls, you can set the value to the property
at design-time: DisplayName on diaplayNameExtender1.
Then at run-time, whenever you want to get the value of DisplayName for a control, it's enough to find it this way:
var displayName = displayNameExtender1.GetDisplayName(control);
Here is the code for DisplayNameExtender component:
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
[ProvideProperty("DisplayName", typeof(Control))]
public class DisplayNameExtender : Component, IExtenderProvider
{
private Hashtable displayNameValues = new Hashtable();
public bool CanExtend(object extendee)
{
return (extendee is Control && !(extendee is Form));
}
public string GetDisplayName(Control control)
{
if (displayNameValues.ContainsKey(control))
return (string)displayNameValues[control];
return null;
}
public void SetDisplayName(Control control, string value)
{
if (string.IsNullOrEmpty(value))
displayNameValues.Remove(control);
else
displayNameValues[control] = value;
}
}

Required Field Validator For Image Control

Old Title: Prevent dynamically created control from retaining value
Old Info:
The reason I need to do this is because I am trying to create a work around for a required field validator for an Image control. The way my code works is I have a Image control beside a Button, a user clicks on the button and then is prompted to upload an image. I need to ensure that an image is uploaded before the user can move onto the next stage.
Since there is no required field validator for an image control, I created a textbox which is suppose to display the imageURL of the image control every time the image control is recreated on postbacks. However, the textbox always retains the value from the initial creation of the control.
* Note: all controls on the page are dynamically created.
The first thing I do is create the image control and add it to an HTML Table. This works fine. Right after that I find the table cell and add the textbox to the cell that has an image control:
HtmlTableCell tc = (HtmlTableCell)customProperties.FindControl("tcControl_" + (i + 1).ToString());
RadBinaryImage rbi = (RadBinaryImage)customProperties.FindControl("CustomControl" + (i + 1).ToString());
TextBox photoValue = new TextBox();
photoValue.ID = "CustomControl" + (i + 1).ToString() + "_txt";
photoValue.Text = rbi.imageUrl;
This occurs everytime I create all the controls. For all the controls they all retain their values, this is the only control which I don't want this to happen. Does anyone know of how this can be done? Or another way of validating an image control?
Thanks for your time,
All comments/answers are appreciated (:
SOLVED:
I created a modified version of a checkboxlist required field validator that I found here.
Here is the code: I replaced the namespace with ######## for security reasons.
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using Telerik.Web.UI;
namespace #######################################
{
public class RequiredFieldValidatorForImages :
System.Web.UI.WebControls.BaseValidator
{
private Control _ctrl;
public RequiredFieldValidatorForImages()
{
base.EnableClientScript = false;
}
protected override bool ControlPropertiesValid()
{
Control ctrl = FindControl(ControlToValidate);
if (ctrl != null)
{
_ctrl = (Control)ctrl;
return (_ctrl != null);
}
else
return false; // raise exception
}
protected override bool EvaluateIsValid()
{
try
{
Image rbi = (Image)_ctrl;
return rbi.ImageUrl != "~/images/noimages.jpg";
}
catch
{
RadBinaryImage rbi = (RadBinaryImage)_ctrl;
return rbi.ImageUrl != "~/images/noimages.jpg";
}
}
}
}
Solved:
I created a modified version of a checkboxlist required field validator that I found here.
Here is the code: I replaced the namespace with ######## for security reasons.
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using Telerik.Web.UI;
namespace #######################################
{
public class RequiredFieldValidatorForImages :
System.Web.UI.WebControls.BaseValidator
{
private Control _ctrl;
public RequiredFieldValidatorForImages()
{
base.EnableClientScript = false;
}
protected override bool ControlPropertiesValid()
{
Control ctrl = FindControl(ControlToValidate);
if (ctrl != null)
{
_ctrl = (Control)ctrl;
return (_ctrl != null);
}
else
return false; // raise exception
}
protected override bool EvaluateIsValid()
{
try
{
Image rbi = (Image)_ctrl;
return rbi.ImageUrl != "~/images/noimages.jpg";
}
catch
{
RadBinaryImage rbi = (RadBinaryImage)_ctrl;
return rbi.ImageUrl != "~/images/noimages.jpg";
}
}
}
}

Rich Text box binding [duplicate]

To do DataBinding of the Document in a WPF RichtextBox, I saw 2 solutions so far, which are to derive from the RichtextBox and add a DependencyProperty, and also the solution with a "proxy".
Neither the first or the second are satisfactory. Does somebody know another solution, or instead, a commercial RTF control which is capable of DataBinding? The normal TextBox is not an alternative, since we need text formatting.
Any idea?
There is a much easier way!
You can easily create an attached DocumentXaml (or DocumentRTF) property which will allow you to bind the RichTextBox's document. It is used like this, where Autobiography is a string property in your data model:
<TextBox Text="{Binding FirstName}" />
<TextBox Text="{Binding LastName}" />
<RichTextBox local:RichTextBoxHelper.DocumentXaml="{Binding Autobiography}" />
Voila! Fully bindable RichTextBox data!
The implementation of this property is quite simple: When the property is set, load the XAML (or RTF) into a new FlowDocument. When the FlowDocument changes, update the property value.
This code should do the trick:
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
public class RichTextBoxHelper : DependencyObject
{
public static string GetDocumentXaml(DependencyObject obj)
{
return (string)obj.GetValue(DocumentXamlProperty);
}
public static void SetDocumentXaml(DependencyObject obj, string value)
{
obj.SetValue(DocumentXamlProperty, value);
}
public static readonly DependencyProperty DocumentXamlProperty =
DependencyProperty.RegisterAttached(
"DocumentXaml",
typeof(string),
typeof(RichTextBoxHelper),
new FrameworkPropertyMetadata
{
BindsTwoWayByDefault = true,
PropertyChangedCallback = (obj, e) =>
{
var richTextBox = (RichTextBox)obj;
// Parse the XAML to a document (or use XamlReader.Parse())
var xaml = GetDocumentXaml(richTextBox);
var doc = new FlowDocument();
var range = new TextRange(doc.ContentStart, doc.ContentEnd);
range.Load(new MemoryStream(Encoding.UTF8.GetBytes(xaml)),
DataFormats.Xaml);
// Set the document
richTextBox.Document = doc;
// When the document changes update the source
range.Changed += (obj2, e2) =>
{
if (richTextBox.Document == doc)
{
MemoryStream buffer = new MemoryStream();
range.Save(buffer, DataFormats.Xaml);
SetDocumentXaml(richTextBox,
Encoding.UTF8.GetString(buffer.ToArray()));
}
};
}
});
}
The same code could be used for TextFormats.RTF or TextFormats.XamlPackage. For XamlPackage you would have a property of type byte[] instead of string.
The XamlPackage format has several advantages over plain XAML, especially the ability to include resources such as images, and it is more flexible and easier to work with than RTF.
It is hard to believe this question sat for 15 months without anyone pointing out the easy way to do this.
I know this is an old post, but check out the Extended WPF Toolkit. It has a RichTextBox that supports what you are tryign to do.
I can give you an ok solution and you can go with it, but before I do I'm going to try to explain why Document is not a DependencyProperty to begin with.
During the lifetime of a RichTextBox control, the Document property generally doesn't change. The RichTextBox is initialized with a FlowDocument. That document is displayed, can be edited and mangled in many ways, but the underlying value of the Document property remains that one instance of the FlowDocument. Therefore, there is really no reason it should be a DependencyProperty, ie, Bindable. If you have multiple locations that reference this FlowDocument, you only need the reference once. Since it is the same instance everywhere, the changes will be accessible to everyone.
I don't think FlowDocument supports document change notifications, though I am not sure.
That being said, here's a solution. Before you start, since RichTextBox doesn't implement INotifyPropertyChanged and Document is not a DependencyProperty, we have no notifications when the RichTextBox's Document property changes, so the binding can only be OneWay.
Create a class that will provide the FlowDocument. Binding requires the existence of a DependencyProperty, so this class inherits from DependencyObject.
class HasDocument : DependencyObject
{
public static readonly DependencyProperty DocumentProperty =
DependencyProperty.Register("Document",
typeof(FlowDocument),
typeof(HasDocument),
new PropertyMetadata(new PropertyChangedCallback(DocumentChanged)));
private static void DocumentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("Document has changed");
}
public FlowDocument Document
{
get { return GetValue(DocumentProperty) as FlowDocument; }
set { SetValue(DocumentProperty, value); }
}
}
Create a Window with a rich text box in XAML.
<Window x:Class="samples.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Flow Document Binding" Height="300" Width="300"
>
<Grid>
<RichTextBox Name="richTextBox" />
</Grid>
</Window>
Give the Window a field of type HasDocument.
HasDocument hasDocument;
Window constructor should create the binding.
hasDocument = new HasDocument();
InitializeComponent();
Binding b = new Binding("Document");
b.Source = richTextBox;
b.Mode = BindingMode.OneWay;
BindingOperations.SetBinding(hasDocument, HasDocument.DocumentProperty, b);
If you want to be able to declare the binding in XAML, you would have to make your HasDocument class derive from FrameworkElement so that it can be inserted into the logical tree.
Now, if you were to change the Document property on HasDocument, the rich text box's Document will also change.
FlowDocument d = new FlowDocument();
Paragraph g = new Paragraph();
Run a = new Run();
a.Text = "I showed this using a binding";
g.Inlines.Add(a);
d.Blocks.Add(g);
hasDocument.Document = d;
I have tuned up previous code a little bit.
First of all range.Changed hasn't work for me.
After I changed range.Changed to richTextBox.TextChanged it turns out that TextChanged event handler can invoke SetDocumentXaml recursively, so I've provided protection against it. I also used XamlReader/XamlWriter instead of TextRange.
public class RichTextBoxHelper : DependencyObject
{
private static HashSet<Thread> _recursionProtection = new HashSet<Thread>();
public static string GetDocumentXaml(DependencyObject obj)
{
return (string)obj.GetValue(DocumentXamlProperty);
}
public static void SetDocumentXaml(DependencyObject obj, string value)
{
_recursionProtection.Add(Thread.CurrentThread);
obj.SetValue(DocumentXamlProperty, value);
_recursionProtection.Remove(Thread.CurrentThread);
}
public static readonly DependencyProperty DocumentXamlProperty = DependencyProperty.RegisterAttached(
"DocumentXaml",
typeof(string),
typeof(RichTextBoxHelper),
new FrameworkPropertyMetadata(
"",
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(obj, e) => {
if (_recursionProtection.Contains(Thread.CurrentThread))
return;
var richTextBox = (RichTextBox)obj;
// Parse the XAML to a document (or use XamlReader.Parse())
try
{
var stream = new MemoryStream(Encoding.UTF8.GetBytes(GetDocumentXaml(richTextBox)));
var doc = (FlowDocument)XamlReader.Load(stream);
// Set the document
richTextBox.Document = doc;
}
catch (Exception)
{
richTextBox.Document = new FlowDocument();
}
// When the document changes update the source
richTextBox.TextChanged += (obj2, e2) =>
{
RichTextBox richTextBox2 = obj2 as RichTextBox;
if (richTextBox2 != null)
{
SetDocumentXaml(richTextBox, XamlWriter.Save(richTextBox2.Document));
}
};
}
)
);
}
<RichTextBox>
<FlowDocument PageHeight="180">
<Paragraph>
<Run Text="{Binding Text, Mode=TwoWay}"/>
</Paragraph>
</FlowDocument>
</RichTextBox>
This seems to be the easiest way by far and isn't displayed in any of these answers.
In the view model just have the Text variable.
Create a UserControl which has a RichTextBox named RTB. Now add the following dependency property:
public FlowDocument Document
{
get { return (FlowDocument)GetValue(DocumentProperty); }
set { SetValue(DocumentProperty, value); }
}
public static readonly DependencyProperty DocumentProperty =
DependencyProperty.Register("Document", typeof(FlowDocument), typeof(RichTextBoxControl), new PropertyMetadata(OnDocumentChanged));
private static void OnDocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
RichTextBoxControl control = (RichTextBoxControl) d;
FlowDocument document = e.NewValue as FlowDocument;
if (document == null)
{
control.RTB.Document = new FlowDocument(); //Document is not amused by null :)
}
else
{
control.RTB.Document = document;
}
}
This solution is probably that "proxy" solution you saw somewhere.. However.. RichTextBox simply does not have Document as DependencyProperty... So you have to do this in another way...
HTH
Most of my needs were satisfied by this answer https://stackoverflow.com/a/2989277/3001007 by krzysztof. But one issue with that code (i faced was), the binding won't work with multiple controls. So I changed _recursionProtection with a Guid based implementation. So it's working for Multiple controls in same window as well.
public class RichTextBoxHelper : DependencyObject
{
private static List<Guid> _recursionProtection = new List<Guid>();
public static string GetDocumentXaml(DependencyObject obj)
{
return (string)obj.GetValue(DocumentXamlProperty);
}
public static void SetDocumentXaml(DependencyObject obj, string value)
{
var fw1 = (FrameworkElement)obj;
if (fw1.Tag == null || (Guid)fw1.Tag == Guid.Empty)
fw1.Tag = Guid.NewGuid();
_recursionProtection.Add((Guid)fw1.Tag);
obj.SetValue(DocumentXamlProperty, value);
_recursionProtection.Remove((Guid)fw1.Tag);
}
public static readonly DependencyProperty DocumentXamlProperty = DependencyProperty.RegisterAttached(
"DocumentXaml",
typeof(string),
typeof(RichTextBoxHelper),
new FrameworkPropertyMetadata(
"",
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(obj, e) =>
{
var richTextBox = (RichTextBox)obj;
if (richTextBox.Tag != null && _recursionProtection.Contains((Guid)richTextBox.Tag))
return;
// Parse the XAML to a document (or use XamlReader.Parse())
try
{
string docXaml = GetDocumentXaml(richTextBox);
var stream = new MemoryStream(Encoding.UTF8.GetBytes(docXaml));
FlowDocument doc;
if (!string.IsNullOrEmpty(docXaml))
{
doc = (FlowDocument)XamlReader.Load(stream);
}
else
{
doc = new FlowDocument();
}
// Set the document
richTextBox.Document = doc;
}
catch (Exception)
{
richTextBox.Document = new FlowDocument();
}
// When the document changes update the source
richTextBox.TextChanged += (obj2, e2) =>
{
RichTextBox richTextBox2 = obj2 as RichTextBox;
if (richTextBox2 != null)
{
SetDocumentXaml(richTextBox, XamlWriter.Save(richTextBox2.Document));
}
};
}
)
);
}
For completeness sake, let me add few more lines from original answer https://stackoverflow.com/a/2641774/3001007 by ray-burns. This is how to use the helper.
<RichTextBox local:RichTextBoxHelper.DocumentXaml="{Binding Autobiography}" />
Here is my solution based on Ray Burns answer with DataBinding and conversion of a XAML-string to a RichTextBox-Document:
ViewModel
TestText = #"<FlowDocument xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""><Paragraph><Bold>Hello World!</Bold></Paragraph></FlowDocument>";
View
<RichTextBox local:RichTextBoxHelper.DocumentXaml="{Binding TestText}"/>
RichTextBoxHelper
public class RichTextBoxHelper : DependencyObject
{
public static string GetDocumentXaml(DependencyObject obj) { return (string) obj.GetValue(DocumentXamlProperty); }
public static void SetDocumentXaml(DependencyObject obj,
string value)
{
obj.SetValue(DocumentXamlProperty, value);
}
public static readonly DependencyProperty DocumentXamlProperty = DependencyProperty.RegisterAttached
(
"DocumentXaml",
typeof(string),
typeof(RichTextBoxHelper),
new FrameworkPropertyMetadata
{
BindsTwoWayByDefault = true,
PropertyChangedCallback = (obj,
e) =>
{
var richTextBox = (RichTextBox) obj;
var xaml = GetDocumentXaml(richTextBox);
Stream sm = new MemoryStream(Encoding.UTF8.GetBytes(xaml));
richTextBox.Document = (FlowDocument) XamlReader.Load(sm);
sm.Close();
}
}
);
}
Here's a VB.Net version of Lolo's answer:
Public Class RichTextBoxHelper
Inherits DependencyObject
Private Shared _recursionProtection As New HashSet(Of System.Threading.Thread)()
Public Shared Function GetDocumentXaml(ByVal depObj As DependencyObject) As String
Return DirectCast(depObj.GetValue(DocumentXamlProperty), String)
End Function
Public Shared Sub SetDocumentXaml(ByVal depObj As DependencyObject, ByVal value As String)
_recursionProtection.Add(System.Threading.Thread.CurrentThread)
depObj.SetValue(DocumentXamlProperty, value)
_recursionProtection.Remove(System.Threading.Thread.CurrentThread)
End Sub
Public Shared ReadOnly DocumentXamlProperty As DependencyProperty = DependencyProperty.RegisterAttached("DocumentXaml", GetType(String), GetType(RichTextBoxHelper), New FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender Or FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, Sub(depObj, e)
RegisterIt(depObj, e)
End Sub))
Private Shared Sub RegisterIt(ByVal depObj As System.Windows.DependencyObject, ByVal e As System.Windows.DependencyPropertyChangedEventArgs)
If _recursionProtection.Contains(System.Threading.Thread.CurrentThread) Then
Return
End If
Dim rtb As RichTextBox = DirectCast(depObj, RichTextBox)
Try
rtb.Document = Markup.XamlReader.Parse(GetDocumentXaml(rtb))
Catch
rtb.Document = New FlowDocument()
End Try
' When the document changes update the source
AddHandler rtb.TextChanged, AddressOf TextChanged
End Sub
Private Shared Sub TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
Dim rtb As RichTextBox = TryCast(sender, RichTextBox)
If rtb IsNot Nothing Then
SetDocumentXaml(sender, Markup.XamlWriter.Save(rtb.Document))
End If
End Sub
End Class
This VB.Net version works for my situation. I removed thread collection semaphore, instead using RemoveHandler and AddHandler. Also, since a FlowDocument can only be bound to one RichTextBox at a time, I put in a check that the RichTextBox's IsLoaded=True. Let's begin with how I used the class in a MVVM app which uses ResourceDictionary instead of Window.
' Loaded and Unloaded events seems to be the only way to initialize a control created from a Resource Dictionary
' Loading document here because Loaded is the last available event to create a document
Private Sub Rtb_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
' only good place to initialize RichTextBox.Document with DependencyProperty
Dim rtb As RichTextBox = DirectCast(sender, RichTextBox)
Try
rtb.Document = RichTextBoxHelper.GetDocumentXaml(rtb)
Catch ex As Exception
Debug.WriteLine("Rtb_Loaded: Message:" & ex.Message)
End Try
End Sub
' Loaded and Unloaded events seems to be the only way to initialize a control created from a Resource Dictionary
' Free document being held by RichTextBox.Document by assigning New FlowDocument to RichTextBox.Document. Otherwise we'll see an of "Document belongs to another RichTextBox"
Private Sub Rtb_Unloaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim rtb As RichTextBox = DirectCast(sender, RichTextBox)
Dim fd As New FlowDocument
RichTextBoxHelper.SetDocumentXaml(rtb, fd)
Try
rtb.Document = fd
Catch ex As Exception
Debug.WriteLine("PoemDocument.PoemDocumentView.PoemRtb_Unloaded: Message:" & ex.Message)
End Try
End Sub
Public Class RichTextBoxHelper
Inherits DependencyObject
Public Shared Function GetDocumentXaml(ByVal depObj As DependencyObject) As FlowDocument
Return depObj.GetValue(DocumentXamlProperty)
End Function
Public Shared Sub SetDocumentXaml(ByVal depObj As DependencyObject, ByVal value As FlowDocument)
depObj.SetValue(DocumentXamlProperty, value)
End Sub
Public Shared ReadOnly DocumentXamlProperty As DependencyProperty = DependencyProperty.RegisterAttached("DocumentXaml", GetType(FlowDocument), GetType(RichTextBoxHelper), New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender Or FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, Sub(depObj, e)
RegisterIt(depObj, e)
End Sub))
Private Shared Sub RegisterIt(ByVal depObj As System.Windows.DependencyObject, ByVal e As System.Windows.DependencyPropertyChangedEventArgs)
Dim rtb As RichTextBox = DirectCast(depObj, RichTextBox)
If rtb.IsLoaded Then
RemoveHandler rtb.TextChanged, AddressOf TextChanged
Try
rtb.Document = GetDocumentXaml(rtb)
Catch ex As Exception
Debug.WriteLine("RichTextBoxHelper.RegisterIt: ex:" & ex.Message)
rtb.Document = New FlowDocument()
End Try
AddHandler rtb.TextChanged, AddressOf TextChanged
Else
Debug.WriteLine("RichTextBoxHelper: Unloaded control ignored:" & rtb.Name)
End If
End Sub
' When a RichTextBox Document changes, update the DependencyProperty so they're in sync.
Private Shared Sub TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
Dim rtb As RichTextBox = TryCast(sender, RichTextBox)
If rtb IsNot Nothing Then
SetDocumentXaml(sender, rtb.Document)
End If
End Sub
End Class
Guys why bother with all the faff. This works perfectly. No code required
<RichTextBox>
<FlowDocument>
<Paragraph>
<Run Text="{Binding Mytextbinding}"/>
</Paragraph>
</FlowDocument>
</RichTextBox>

Access to a Label on the Form by my Custom control

I have created a WindowsFormControlLibrary porject. It works fine, I can drop it on the forms,call its methods,etc ...
but now as a property of it, I am passing the name of a Label to it. and I want this custom control to be able to use that label name and for example change its font to bold .
so the question is that if I have a WinForm and I have a Label on that form and my custom control on that form, then how can I tell my custom control to do something with that label which I am passing its name to it?
Instead of sending in the name of the label, send in a reference to the actual label and then the custom control can both read the name if it needs to and change the label's font and other properties.
Be careful though, it can quickly get messy to keep track of what's happening if various forms and controls change controls on other forms etc.
Edit: Added code to do what you ask for in the comments
Code isn't tested so it might not be completely correct, but something similar to this should work.
foreach (Control c in Parent.Controls)
{
if (c is Label)
{
Label l = (Label)c;
// do stuff to label l
}       
}
First, if you wish to access a Control from your UserControl, you will need to use the FindForm() method.
Second, you will be required to expose your TextBox control, for example, through a property of your form.
Then, you would need to know the type of this Form returned by this FindForm() method.
Once you know it, you need to type-cast this result to the correct type.
So, here a sample untested pseudo-code to give you the idea:
public partial class MyMainForm {
private TextBox textBox1;
public MyMainForm() {
textBox1 = new Textbox();
textBox1.Name = #"textBox1";
textBox1.Location = new Point(10, 10);
textBox1.Size = new Size(150, 23);
this.Controls.Add(textBox1);
}
public Font MyTextBoxFont {
get {
return textBox1.Font;
} set {
if (value == null) return;
textbox1.Font = value;
}
}
}
Then, assuming you have dropped your control on your form, your UserControl could have a property like so:
public partial class MyUserControl {
private Form GetContainerForm {
get {
return this.FindForm();
}
}
// And later on, where you need to set your TextBox's font:
private void SetContainerInputFieldFont(Font f) {
if (GetContainerForm == null) return; // Or throw, depending on what you need to do.
((MyMainForm)GetContainerForm).MyTextBoxFont = f
}
}
cool :) I just added a get set public property of type Label... it automatically lists all the label on the form.

Categories

Resources