I'm trying to work out how to catch a data binding in the act (either intercept or post-process) to customize the display of data in the target control.
I know about IValueConvertor and understand that I can transform a simple value into another simple value, but I don't believe this is enough for my needs... which are:
The control in this case is a TextBlock and the data values from the objects in my ObservableCollection are variable length strings. I want to render the strings in multiple colours by splitting them into pieces and programatically creating a <Run Foreground="xxx" Text="yyy"/> for each piece inside the TextBlock.
Since the strings are variable length and the colours have to be programatically determined from the content of the string, I don't believe I can pre-create the <Run>s in the XAML, so I have to somehow get in on the data binding action and generate the <Run>s at bind-time (or very soon after).
Binding.NotifyOnTargetUpdated would seem to be a way to set up an event handler to do the work, but that's not available in the Windows Phone cut-down Silverlight implementation.
Any ideas? All search results seem to point to the above, but I'm looking for that little bit more.
Having apparently exhausted all the cut-down Silverlight on Windows Phone options for programmatically hooking the rendering of ListBoxItems via the data model, I ended up adding a Loaded="..." event handler to the XAML for the TextBlock.
It doesn't feel like the nicest solution, but perhaps that's just my code-preference talking and it's actually the right way to do it on Windows Phone.
In any case, because I'm hooked to the TextBlock directly, I'm not sure how to then get to the databound object on the ListBoxItem containing the TextBlock... if anyone has advice on how to get back up the tree to the generated ListBoxItem then I could use the bound object directly instead of retrieving it from elsewhere.
Note that since the ListBoxItem is generated, I didn't find where to put a Loaded="..." event handler for that in the XAML. The ListBox.ItemTemplate doesn't accept a Loaded attribute.
Update: this doesn't work!
The Loaded event handler fires when the TextBlock is first created and loaded, so the substitution works initially.
BUT
The generated ListBoxItem seems to be recycled (I guess by the ListBox.ItemContainerGenerator which doesn't want to use excessive amounts of memory by instantiating a whole new container when there are many off-screen entries in the list that won't need to be seen for a while) and when this happens, the Loaded event DOES NOT FIRE.
Since I modify the content when the TextBlock was first Loaded, this breaks the binding association so when the ListBoxItem is recycled, it now contains old/incorrect data.
Still no solution.
I'm thinking about trying to use an IValueConvertor and somehow pass a reference to the binding target... now sure how yet though.
Update 2: finally got it to work...
Sticking with the Loaded="..." event handler, it is possible to disable recycling of previously-generated ListBoxItems by configuring the VirtualizingStackPanel used by the ListBox under the covers.
In the XAML for the ListBox set VirtualizingStackPanel.VirtualizationMode="Standard" to force a new ListBoxItem to be generated each time instead of recycling previously-generated ones.
This means the Loaded event handler is called every time and I can replace the ordinary text of the TextBlock with the <Run>s to produce dynamically coloured text.
Related
This is a kind of GUI automation application whereby I want to read the data from a listview from another process.
The listview class is SysListView32 and has following styles set LVS_OWNERDRAWFIXED
Generally I am able to read the text from listview using the following procedure
Allocate memory in the memory space of other process
Send message to listview to read the text with the pointer of buffer allocated in that process
Read the buffer
It works fine when the listview is not ownerdrawn but in this case, the listview appears to be drawn by the owner, i.e. the listitem has no data.
Is it possible to read the text from such a listview either by the method I have discussed or by any method or by hooking the api or whatsoever method ?
The control must still add LVITEMs to the list view. But of course there's no obligation to put anything useful in them. Specifying a null pszText or iImage would work just fine if the app does its own drawing. It will implement a WM_DRAWITEM message handler and use internal data to render the item.
There is no way to find out where that data is stored. You could fake your own WM_DRAWITEM message, albeit that it is very hard to do since you must inject code to create the HDC, but that just gets you pixels, not bytes. Using OCR would be a major outlier solution. Realistically you'll need to throw in the towel on this one.
I have a hierarchical flexgrid control with the ToolTipText property set, and when I run from source the tooltip displays as it should. But when I compile it and run that way, the tooltip doesn't display.
I've tried to remove anything listening to MouseMove in the hopes that that would fix it, and when I add some code to put the tooltip text into a message box, it appears to be set correctly. Can anyone think of why this would be happening?
Update: It appears that the problem arises when I host the grid inside another user control. E.g.: make container.ctl, which is just a blank control but with ControlContainer = True. Then make gridholder.ctl, which is a mshfg inside of a container.ctl. Lastly, embed gridholder.ctl into some form. Tooltips on the flexgrid don't appear to show up.
I'm interested to see how reproducible this is...
I haven't found a workaround for this issue yet, but I have a better idea of why it's happening after some testing and stepping through some of the VB6 runtime code in WinDBG.
The first interesting thing is that VB6 doesn't use the standard tooltip display mechanisms provided by Windows. For example, it doesn't use WM_NOTIFY messages to show/hide tooltips, or any of the other "standard" tooltip support described in the documentation explaining how tooltips work in Windows.
Instead, the VB6 runtime has its own way of managing and displaying tooltips. In principle, it's similar to in some ways to the standard Windows way of dealing with tooltips, but it's also different in a quite a few areas.
A breakdown of how VB6 does tooltips:
When a VB6 program starts, the runtime uses SetWindowsHookEx to install a mouse hook for the program's main thread.
The mouse hook intercepts all mouse messages sent to the program, in particular all WM_MOUSEMOUSE messages
Whenever the mouse hook runs, it calls an internal method in the VB6 runtime to get the object pointer (HCTL) of the control that the mouse is currently over top of. Note that this is an actual COM interface pointer, not a window handle.
It translates the HCTL to the corresponding window handle (HWND).
It checks to see if the mouse position is within that window's rectangle.
If so, it retrieves the ToolTipText property for the control. If this is not empty, it creates a tooltip window and displays the tooltip after a 700ms delay.
The problem with the MSHFlexGrid (and I imagine other controls that are not standard VB6 controls) is that this code doesn't retrieve the correct HCTL when you hover over the control and it's inside a custom container.
In that case, the code retrieves the HCTL of the custom container, not the HCTL of the MSHFlexGrid itself. Therefore, it retrieves the container's ToolTipText property (which is empty) and not the grid's ToolTipText, and therefore won't display a tooltip.
I'm not sure exactly why it does this, because as noted in the comments on your question, all of this works correctly if you use a PictureBox as your container.
I suspect the PictureBox has code to handle this correctly that is not included when you create your own container.
I'll update this answer with an actual workaround if I can find one. The only thing I can think right now is to somehow "sync" your container's ToolTipText property with the grid's ToolTipText property, so that when VB6 requests the container's ToolTipText, it will return the value of the grid's ToolTextTip property instead.
This is easier said than done, however, because ToolTipText is an extender property, and extender properties take precedence over properties that you write yourself that have the same name.
After a bit of research, I found what I think is the underlying problem. Your user control is not implementing any method for the controls to interact with. User Controls that are Container Controls need to implement the Extender functionality. These two links are the best I've found on the subject so far.
http://www.justvb.net/obook/ch7.htm#UsingtheExtenderObject
http://msdn.microsoft.com/en-us/library/aa733622(v=vs.60).aspx
I try to migrate my iphone app to wp7.
On ios, UITableview is well designed, and very easy to code.
I have the some book about wp7, but there are no detail concerned about moving and removing of a row of listbox.
Welcome any comment
I'd like to think that Windows Phone is also well designed. :)
To simply remove an item from a ListBox in your codebehind, you need to get a reference to the ListItem that you want to remove. For example, to remove the currently selected item:
var myListItem = myListBox.SelectedItem;
Then you can remove it with:
myListBox.Items.Remove(myListItem);
In order to address your question about moving items, we'd need to know more about how you're populating the ListBox. Are you using databinding, or adding items with myListBox.Items.Add(myListItem)?
If I needed to move items between two lists, I would create a public List<MyItem> property for each list and databind each list to its own ListBox. Then I might do something like this (working from memory here):
var itemToMove = myList1.Remove(myListBox1.SelectedItem);
myList2.Add(itemToMove);
myListBox1.DataBind();
myListBox2.DataBind();
There's a much better way to do this, using INotifiyPropertyChanged, but I suspect you need to play around with the basics for a while to get comfortable in C#.
Bonus tip: Windows Phone is very similar to Silverlight, and you can usually find more information if you search for Silverlight examples.
In WP7, usually we set the ItemSource of listbox, when our source changes its changes automatically propagate to the UI due to data binding ( it is known as one way binding ; source to UI ).
so what ever you do ( add, update or delete) in itemsource, it reflects to UI.
databinding in wp7
I'm using the LoopingSelector to show a series of timestamps. As time progresses, I'd like to update the timestamps in the looping selector.
I have a class that implements the ILoopingSelectorDataSource interface, but I can't figure out how to force the data to reload while the control is showing.
Claus is partially correct, there doesn't seem to be a way to use binding to update the control, but in code behind, calling
LoopingSelector.DataSource = new MyLoopingSelectorDataSource()
// your implementation of the ILoopingSelectorDataSource interface
will cause the control to reload data.
Basically you can't. The control doesn't update the currently rendered controls. You would have to rewrite the control to fit your purpose.
While you can easily make it calculate the time relatively to a given point upon the call to GetNext() or GetPrevious(), updating the elements already on the screen would require a invoke of a kind, or a constant update per second/minute or whatever fast you want them to update.
Perhaps hoping in a miracle, but let's try :-)
MyControl derives from Control. Its ControlTemplate contains
<ContentPresenter ContentTemplate="{TemplateBinding EditorTemplate}"/>
(Other details are omitted.)
Derived controls supply suitable EditorTemplate. For example MyTextControl specifies template consisting of a TextBox. (With proper bindings, of course.)
I won't describe what works (most scenarios), but what does not:
A collapsed MyTextControl instance is created. Later on this control is made visible. Here is what happens:
MyTextControl instance created, set to Collapsed
MyTextControl.Loaded event: At this moment the visual tree contains MyTextControl with no children.
In the Loaded handler I call ApplyTemplate(). In turn the visual tree is modified to MyTextControl -> ContentPresenter. That's all, no more children.
Stil in Loaded handler, I assign Loaded handler to the ContentPresenter.
Sometimes later the control is made visible. Its visual tree gets populated by TextBox internals: Border -> ContentControl -> ContentPresenter -> internal TextBoxView. In other words the control just works.
The problem is that the ContentPresenter Loaded handler was not called, i.e. I am not able to identify the moment when the control is ready.
I tried an alternative solution, i.e. instead of forcing ApplyTemplate() I simply waited in MyControl.OnApplyTemplate(). The sequence:
MyTextControl instance created, set to Collapsed
In the Loaded handler the visual tree contains MyTextControl with no children.
The control is made visible.
In OnApplyTemplate() the visual tree is MyTextControl->ContentPresenter.
Stil in OnApplyTemplate(), I assign Loaded handler to the ContentPresenter.
The rest is as before. The visual tree is populated by TextBox internals (the control works), but the above handler gets not called.
Does anybody know a way how to identify the moment when the control is fully loaded?
Note that I did the above with several other MyControl-derived controls. For each of them one of the above scenarios worked (sometimes one, sometimes another), but the TextBox-based control is the first one where I am not able to identify moment of loading.
Also note that this problem does not happen when the control is visible for all the time.
Ok, I'll answer myself. My current conclusion after 1 day and dozens of tests is that Loaded event is for birds. It happens on various stages of the control life cycle and in case of composite controls there is no warranty that the control is fully functional. In some cases it might be not fired at all.
Forcing template building by calling ApplyTemplate() is no solution either as in some cases it may result in building partial control tree.
OnApplyTemplate suffers similar problems - it might be called when only partial control tree is built.
After acknowledging the above statements I decided to give a try to LayoutUpdated event. I set up the handler in OnApplyTemplate() (I tried to use latest possible moment) and investigated the control tree. As a first approximation it seems to be sufficient to check if the ContentPresenter has children. If so, we'll say that the control is loaded and unregister LayoutUpdated handler. A more sofisticated test could be used, but the trivial one I just described is working for wide range of controls.
Originally I was afraid that the LayoutUpdated solution will be inefficient, but it looks like (using the described organization) the first handler call is exactly the place when the control is "loaded".