GotFocus events on controls within a RichTextBox FlowDocument

hi guys,

i have a FlowDoc within a RTB which i create and manage via code (no xaml). i have multiple blocks within my document, some of which contain Table objects with multiple TableCell objects. I desperately require to have GotFocus and LostFocus events fire from my TableCell and Block objects, however for whatever reason i just cant seem to make it work.

as both objects are created dynamically, i've resorted to the following:
    Dim currentRow As TableRow = New TableRow()
    For i As Integer = 1 To numberOfCols
      Dim cell As New TableCell(New Paragraph(New Run("")))
      currentRow.Cells.Add(cell)
      LoadStyles(cell)
      AddHandler cell.GotFocus, AddressOf ConvertToCurrency
    Next

Open in new window

...where ConvertToCurrency is my method i need called when GotFocus is fired. my RTB itself has its IsDocumentEnabled value set to true.

so what am i missing? any help would be greatly appreciated.

cheers
gem56Asked:
Who is Participating?
 
Bob LearnedConnect With a Mentor Commented:
How's 'bout using the RichTextBox.SelectionChanged event to query the Paragraph, get the Run, and apply formatting:

Private Sub richTextBox1_SelectionChanged(sender As Object, e As RoutedEventArgs)
	Dim paragraph As Paragraph = richTextBox1.CaretPosition.Paragraph

	Dim run As Run = TryCast(paragraph.Inlines.FirstInline, Run)

	Me.DebugText.Text = run.Text
End Sub

Open in new window


<Window x:Class="WpfApplication1.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" Loaded="Window_Loaded" >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="1*" />
            <RowDefinition Height="10*" />
        </Grid.RowDefinitions>
        <RichTextBox Name="richTextBox1" Grid.Row="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" SelectionChanged="richTextBox1_SelectionChanged" IsDocumentEnabled="True" />
        <TextBox Name="DebugText" Grid.Row="1" HorizontalAlignment="Stretch" Background="#FFD4CCCC" TextWrapping="Wrap" />
    </Grid>
</Window>
0
 
Bob LearnedCommented:
What that sounds like is that the TableCell is not what the RichTextBox sets focus on...did you try to check with the Paragraph or Run?
0
 
gem56Author Commented:
hello TheLearnedOne, thanks for your reply.

i initially assumed something similar as i read something about focus functions getting an overhaul in wpf, unfortunately it doesnt look like thats exactly it. i've added handlers to the GotFocus events for everything starting at the Run and to every control all the way up to the RTB. it looks like only the rtb itself will fire the gotfocus event.

interestingly enough i can get the TableCell's to fire on MouseEnter events, so i dont think its a matter of controls within the rtb not being able to fire...it just doesnt fire on GotFocus for whatever reason.

any ideas?
cheers
0
Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
Bob LearnedCommented:
Oh yeah, focus is a lot trickier with WPF that it ever was with Windows Forms, but there is a lot more going on under the hood.  There is a the concept of keyboard focus and mouse focus, focus scopes, etc..  The behavior of the focus changes when the mouse is captured.  The TextBoxBase class creates challenges that are tough to overcome.

Here is an article that talks about one such scenario:

Why is focus in WPF so tricky? [managing and understanding focus in a WPF application]
http://www.pluralsight-training.net/community/blogs/eburke/archive/2009/03/18/why-is-focus-in-wpf-so-tricky-managing-and-understanding-focus-in-a-wpf-application.aspx

You can use the MouseDown event for a Run, and it works:


Imports System.Windows
Imports System.Windows.Documents
Imports System.Windows.Input

	''' <summary>
	''' Interaction logic for MainWindow.xaml
	''' </summary>
	Public Partial Class MainWindow
		Inherits Window
		Public Sub New()
			InitializeComponent()
		End Sub

		Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
			Dim document As New FlowDocument()
			document.Focusable = True

			Dim table As New Table()

			Dim rowGroup As New TableRowGroup()

			table.RowGroups.Add(rowGroup)

			Dim row As New TableRow()

			rowGroup.Rows.Add(row)

			Dim cell As New TableCell()

			row.Cells.Add(cell)

			Dim paragraph As New Paragraph()

			Dim run As New Run()
			run.MouseDown += New MouseButtonEventHandler(AddressOf run_MouseDown)

			run.Text = "This is a test of the radio broadcast system"

			paragraph.Inlines.Add(run)

			cell.Blocks.Add(paragraph)

			document.Blocks.Add(table)

			Me.richTextBox1.Document = document
		End Sub

		Private Sub run_MouseDown(sender As Object, e As MouseButtonEventArgs)
			Dim run As Run = TryCast(sender, Run)
			MessageBox.Show(run.Text)
		End Sub

	End Class

Open in new window

0
 
gem56Author Commented:
hello again TheLearnedOne,

thanks for the link but i've already read that page a couple of times now over the last week while bashing my head against my desk trying to figure this out :)

ok i know the MouseDown events work on Run objects (from memory i think also on TableCell's...or at least MouseEnter events work), however my problem is i need "keyboard" focus...and the user is able to navigate they're way around within my rtb purely via keyboard (tabs, arrow keys, etc), in which case i would need supplementary event fires along with MouseDown events to simulate a "normal" GotFocus event...which from my understanding should be handled from a GotKeyboardFocusEvent anyway.

my second problem would be that i need event firing on LostFocus as well, so by using a workaround method i would then need to start keeping track of my current/last 'active' TableCell and keep comparing every time i use my work around event(s).

my third problem is that i need basically the same GotFocus/GotKeyboardFocus handlers to register event firings on my FlowDocument.Blocks objects to execute similar methods.

i fear there has to be a simple'ish solution to this crazyness otherwise building a work around solution is going to be a nightmare..not to mention a major speed hit.

anyways, appreciate the suggestion..hopefully theres something more suitable/efficient out there...
0
 
gem56Author Commented:
..just as a side note, i've tried registering class handlers for TableCells, Paragraphs and Runs on GotFocus and GotKeyboardFocus events with "handledEventsToo" set to True just in case somethings handled internally...none seem to fire :(
0
 
gem56Author Commented:
Hello TheLearnedOne,

unfortunately that solution isnt viable as my flowdoc can be up to about 30 - 40 pages. so unless theres a way within that routed event to determine whether the selection change was caused by certain keys (arrow keys, home, end, page up, page down) or a mouse click event, then i'd be facing a major speed hit checking the format of my run text and keeping track of my last active tablecell, etc etc.

any other ideas?
cheers
0
 
Bob LearnedCommented:
You lost me on that last one...why would you need to know the reason for the selection change?  I think this is a case where I don't have the "bigger picture"...
0
 
gem56Author Commented:
sorry mate. ok the idea is that we have an rtb with a flowdoc. the content within the flowdoc will basically be a combination of tables (and table data) and normal word text. so basically something similar to what would be a ms word/excel blend.

the problem is two fold. one problem is that whichever tablecell (within any table in the flowdoc) has focus needs its text formatted (methods already exist) based on whichever column it belongs to whenever it gains and loses focus. for example we have various columns, some columns formatted to currency. the idea is that for these 'currency' cells, whenever the user selects/focuses into them, the text reformats from currency to plain numeric...and when they lose focus, the text formats back to currency.

the second problem is that as the flowdoc has essentially two different "input modes" (table and text), we need to keep track of the current mode as both modes have their own behavioural characteristics. only one input mode exists for each flowdoc block. so for example the first 3 blocks of our flowdoc contains normal text, the forth block contains a table. we need to know which block the caret is in so when the user presses the enter key, we know which method to execute.

the idea was that for problem 1 we could write some GotFocus and LostFocus events for our TableCell objects that would handle the formatting, and for problem two we could write another GotFocus event for our Block objects that would basiaclly check the input mode and store it.

the bigger problem here is because our flowdoc will be up to about 30 pages worth, implementing a KeyDown or SelectionChange event would be very time costly as we'd basically have to keep track of which object has focus ourselves and recalculate every time a keys pressed to determine if focus changed, etc etc.
0
 
Bob LearnedCommented:
"implementing a KeyDown or SelectionChange event would be very time costly as we'd basically have to keep track of which object has focus ourselves and recalculate every time"

That doesn't make sense, since what I showed you would be all that you would need to keep track of the current paragraph, and from that you should be able to get other information, such as owner TableCell.

Private Sub richTextBox1_SelectionChanged(sender As Object, e As RoutedEventArgs)
	Dim paragraph As Paragraph = richTextBox1.CaretPosition.Paragraph
	Dim run As Run = TryCast(paragraph.Inlines.FirstInline, Run)

        If run Is Nothing Then
             Throw New NullReferenceException("Could not get Run from Paragraph")
        EndIf

        Dim cell As TableCell = TryCast(paragraph.Parent, TableCell)

        If cell Is Nothing Then
             Throw New NullReferenceException("Could not get TableCell from Paragraph")
        EndIf
End Sub

Open in new window

0
 
gem56Author Commented:
Hello TheLearnedOne,

yes I'm aware that I'll be able to track the current paragraph and therefore TableCell, however i need to simulate a LostFocus event on my TableCells to change the text formatting within the cell's run when the user moves into a different cell. Therefore without a "real" GotFocus and LostFocus event firing, then I'll need to keep track of the "current" TableCell that has focus, then check it every time a KeyDown or SelectionChange event fires, check if the 'last' TableCell is the 'current' TableCell and if not call my own LostFocus method on the last TableCell.

This I assume would be the time consuming part as having to check and compare the last active and current TableCell objects every SelectionChange will get time consuming. Using a KeyDown i expect might be quicker as we could check if the KeyDown 'key' is an arrow key or home/end/etc...although a KeyDown event doesnt cater for changing the caret position with a mouse click.

at this point im guessing that the internal functionality doesnt exist for TableCell objects to trigger GotFocus and LostFocus events, in which case what would be more time consuming processing wise: a KeyDown (checking is key is Home, End, Page Up, Page Down, Arrow) + MouseClick combination, or a SelectionChange ?

cheers
0
 
Bob LearnedCommented:
Would it be possible to show me what you are working with?  Focus is pretty tricky in WPF, and I have no idea how to override the default behavior for the TextBoxBase.
0
 
gem56Author Commented:
hi TheLearnedOne,

i've just sent an email to your experts exchange account. the password for the archive is just your username.

cheers
0
 
Bob LearnedCommented:
The question has gone a little past my understanding of WPF, and I haven't had much time to devote to research...
0
 
gem56Author Commented:
hey look no dramas mate. im guessing theres probably just no nice way of doing it and i'll have to build some sort of work around for it. i'll keep the question open for a few more days in case anyone else can provide a workable solution, otherwise i'll just award it to you for all your help :)

cheers TheLearnedOne
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.