Binding Issue with custom control inside a RichTextBox - c#

I'm totally stumped by this one.
Here's the setup:
RichTextBox that has your standard runs, and paragraphs. When user selects some text, hits a hotkey, I replace the selected text with a custom control derived from InlineUIContainer - let's call it CustomContainer.
CustomContainer has a single child of type TextBlock. TextBlock's TextProperty is bound to an object whose property changes.
This was done so that the text of this property cannot be edited - if you select it, or try to delete it, it deletes the entire chunk rather than any characters.
All this works beautifully.
Here's the problem:
When you click Save we take the entire RichTextBox data, serialize it, and save it to a text file. When you then reopen the file, all the bindings are re-wired as needed, and everything looks as it should.
Until the property that's bound to is changed.
At this point CustomContainer does not change. Remember, CustomContainer has a TextBlock with the Text property bound. If I look at the TextBlock in question, I can see the Text property has changed - however, visually nothing has changed. I've tried doing various things to refresh the container, but nothing seems to work. I've tried UpdateLayout, I've tried changing the binding, but nothing does the trick.
If I go and delete that CustomContainer from the RichTextBox and then highlight and hit the hot-key everything works. So the code and the binding works. Somewhere in the deserialization, rebinding, and updating of the RichTextBox, the painting of the property as it changes stops working...
UPDATE: I've managed to get it working, though I consider it a bit of a hack. I'm going to keep it open in case someone can provide me with a real solution.
What I've done is removed the custom control on load, and create a new one from scratch and insert at the same position. This is doing the trick, though I'm confused as to why (it only amounted to just calling the constructor, removing the old and adding the new. The rest of the code with the binding is all the same, so I'm still confused why this would work - or rather, why the other way doesn't)

Related

Why are some properties unaccessable

I was trying to get the text that I wrote in a DataGrid cell after editing it, so I put a breakpoint in the function CellEditEnding and looked at the EventArgs and noticed that it contains the property "Text", so I wouldn't have to do the usual XAML binding hacks to get it.
However, I quickly noticed that it will not let me access it.
After taking a look at the FrameworkElement class, I can confirm that there is no Text property, so what is going on, why can't I acces the property?
why can't I acces the property?
Because a FrameworkElement indeed has no Text property.
TextBox, which derives from FrameworkElement, has a Text property though so you could cast the EditingElement to a TextBox and then access the property:
string text = (e.EditingElement as TextBox)?.Text;
Visual Studio displays the properties of the actual object in memory.
It's a bad idea to use the UI as a data store and try and directly work with it.
You should bind an observablecollection of t to the itemssource of your datagrid and work with each instance of t.
That will be far easier to work with.
As to why are some properties inaccessible?
It's because those properties aren't where you think they are. The DatagridCell has a series of things nested within it.
DataGridCell > Border > ContentPresenter > TextBlock
Download snoop https://github.com/snoopwpf/snoopwpf or install using chocolatey / your preferred method.
Run snoop.
Run your app.
Drag the right gunsight thing over you window.
A window should open up with two panels. Controls and properties.
Mouse over a datagrid cell.
press shift+ctrl and you should see the element under the mouse selected in the ui tree.
A datagrid is pretty complicated and there are multiple things in each row.
See that textblock there?
That's the thing has a text property.
Or at least that's the thing when you're not in edit mode.
Switch to edit mode and I have a TextBoxView.
So one complication is, which are you working with at a given time?

In Winforms, whenever I edit the form I lose other changes I've made to Designer.cs, but I can't figure out how to add a "Resize Event" normally

I'm trying to get a resize event working, and I tried just adding "this.Reszie += whatever" and that worked fine, but whenever I make other changes to the Form through the editor, it completely erases that line (as well as anything else in the Form1.Designer.cs class). My questions is, how can I edit this as intentioned, like how double clicking on a button or text box will automatically do this all? Thanks
You can select event handlers in the properties window. You must click on the flash symbol, to switch form properties to events view.
Also, before you do manual changes to ".designer.cs", close the form designer (because saving the form overwrites the ".designer.cs" file).
Usually, you should avoid doing changes in the ".designer.cs" file, but there are rare occasions where this is helpful. E.g., you have used a TextBox and want to replace it by a custom textbox or third-party textbox. Then changing the type manually will allow you to do it without removing and re-adding these controls.

How to reset all items in a groupbox, but not any items outside said groupbox?

I'm working a WPF application which has a number of groupboxes, each containing 3-4 controls a piece. Each of these groupboxes contains a reset button.
Instead of explicitly writing a line for each item the groupbox reset buton should reset is there a way to have the reset button only reset all items for the groupbox it's contained upon?
When researching this issue I found the following post which seems relevant but I'm unsure how to extend Nathan's answer to do this: What is the best way to clear all controls on a form C#?
You can use this question as an source of methods you need.
First of all you need to find groupbox that contains button. To do that use sender argument form click event it is reference to button that was clicked and call FindVisualParent method from one of answers in that question. This will give you groupbox.
Then Find all ui elements by FindVisualChildren<DependencyObject> method. This will give you super set of controls that are to be cleared.
Then it is just a loop over this as in question that you referenced. Note that that question was intended for winforms not wpf so you will have to revise the controldefaults dictionary which handles grouping controls ~(Panel GroupBox) which is not needed here (and would not work in wpf).
You might add a TAG to each control telling which property is beeing edited in the control, and then on reset press, you travel up the button to find containing GroupBox, then down to find all controls contained in this GroupBox, and then you find, with the TAGs, the name of the properties that needs reset. You then reset the properties using Reflexion.
Maybe an issue is that the default value might not be obvious. But you could store the default values for all your bindings in a static object, and copy the value of the property for this reset object into the current status object.
Rq : you could also use reflexion to get the bindings for all control of current GroupBox, and then get the bounded properties from the binding, so no need for a TAG. BUT if a control has several bindings, you won't know which one to reset. In my case, controls within GroupBoxes have also a color binding to indicate wether they have default value (green) or non-default (red), hence the need for a TAG. (BUT the color indicator are read-only so reflexion could, in fact, also be used with a little more use of Reflexion... was just too lazy to change a working logic when i saw that :-) )
I don't know about your bindings so obviously i cannot tell what's best, i hope this thoughs can help.

C# How to commit a TextBox?

In a form, I have a TextBox Binding an Object on its member property "Title". Along with it is a "Save" button to test the binding.
Seems like the underlying object property does not get updated unless the textbox loses focus. But there no form.ActiveControl.Blur() for use. Besides, this does not seem like a sound hack.
Anyway to do this better? Thanks.
EDIT: Sorry for not being clear. My question is in the title: "How to commit a TextBox". I use the term "commit" from the DataGridView commit and BindingSource commit. And it's in WinForms. (Have never worked with WPF, so it didn't occur to me. Sorry).
The actual scenario I have is I have a bunch of TextBox binded to property of a single Object. The user enters values in all the TextBox and when the user clicks save (toolbar button), the last TextBox is still in focus (or in editing mode) hence the save will not capture the last value in the last textbox.
I want to find the correct way to "commit" the textbox value just before saving.
Thanks.
Since the question has been updated to indicate this is WinForms, you'll need to handle things a little differently than if this were a WPF application. Fortunately, it turns out that the solution is very simple.
Whenever the user clicks on the "Save" button (so, say, in your Save button's Click event handler), you need to call the EndEdit method on your BindingSource. This will cause all pending changes to be committed to the underlying data source, exactly what you were hoping to accomplish.
Also see the relevant documentation on MSDN for more details.
Sounds like WPF from the problem description..
You want to change the binding so that it updates when the property value changes instead of when the textbox loses focus (which is the default when binding to TextBox.Text). You can do this by setting the UpdateSourceTrigger property on your binding:
<TextBox Text="{Binding UpdateSourceTrigger=PropertyChanged}"/>

C# PrintDocument Changed event

My issue is that I've created an "extended" RichTextBox control that uses native API to add many RichEdit features that are missing from the standard control (ie: modifying a single font property on a selection w/o changing the other font properties, word wrap to a printer [like WordPad], etc.). As part of the control I expose a PrintDocument that is used to print the formatted contents of the RichTextBox. When wordwrap is set to "Wrap To Printer" I SendMessage the EM_SETTARGETDEVICE message to the RichTextBox and cause it to wrap to the appropriate length.
This all works fine when something (user/code) changes the WordWrap property of my control. However if the PrintDocument is modified after that I have no way of knowing it. So even though the user may have changed the margins on the PrintDocument my RichTextBoxEx doesn't resend the EM_SETTARGETDEVICE for the new width until the WordWrap property is changed.
I see a few options to overcome this but I'm not a big fan of any of them. Here's what I have:
Add a UpdatePrintDocument() method or similiar that would need to be called after something external from the control (ie: a PageSetupDialog on the parent form) updated the settings in the PrintDocument. Cons: I'll be distributing the control so I'd like to make it as friendly as possible. While I may remember to call the method anytime I successfully update the PrintDocument settings someone else might not. Pro: It's simple to implement.
Create a new, PrintDocumentEx class that bases from PrintDocument and implements the needed "Changed" events. Cons: Might not be enough, might need to create PrintSettingsEx, PageSettingsEx, etc.. Pro: Implement once and no one has to worry about it again.
I really think #2 is the option I'm going to have to go with but it's not very reusable for the next instance I need some similiar functionality. I guess what I'm looking for is a way to attach a "generic PropertyChanged event" to any existing classes property since this would be applicable in future situations. Anxious to see what you guys have for me :-)
If I have understood your question correctly, the information that you require is sent when the WordWrap property is changed.
When other things are changed, no events updates the Print Document. The next time the WordWrap property is changed all information is sent.
The hack way to fix this is then to change the WordWrap property, whenever you change a property that you want to send to the Print Document. Change it to a temporary value, then change it back again.
Just following up that my PrintDocumentEx (and associated) classed didn't work. Looking into the PrintDialog and related controls it's because they use native methods to acctually update the PrintDocument. So the events I attached to the properties in my "Ex" classes never fired because the set accessor was never invoked.

Categories