WPF Tooltip binding not updating - wpf

Good afternoon all,
I have to work with a legacy Winforms application but I'd like to start migrating it to WPF. It doesn't have a tooltip control now so I'd like to use a WPF tooltip object.
I create a single global instance of a tooltip object. I've bound the controls within it and my application sets the datacontext of the tooltip. I can manually show and hide the tooltip just fine. The first time I hover over an object it picks up the bound data perfectly and works great. When I move over another control the tooltip datacontext is changed but the displayed data is never reloaded.
I tried implementing a property changed event and use the INotifyPropertyChanged interface in the object I bind to. It appears the wpf tooltip is not listening to the event.
I tried setting the binding mode to Oneway (it's a display only tooltip).
The tooltip is created programmatically:
// build the tooltip window.
System.Windows.Controls.Image img = new System.Windows.Controls.Image();
img.Width = 50;
img.Height = 60;
// bind the image
System.Windows.Data.Binding imageBinding = new System.Windows.Data.Binding("PatientImage.Data");
imageBinding.Mode = System.Windows.Data.BindingMode.OneWay;
imageBinding.Source = bed;
img.SetBinding(System.Windows.Controls.Image.SourceProperty, imageBinding);
// wrap image in a border
System.Windows.Controls.Border brdr = new System.Windows.Controls.Border();
brdr.BorderBrush = System.Windows.Media.Brushes.Blue;
brdr.Margin = new System.Windows.Thickness(6);
brdr.Child = img;
System.Windows.Controls.WrapPanel wp = new System.Windows.Controls.WrapPanel();
System.Windows.Controls.TextBlock tb = new System.Windows.Controls.TextBlock();
tb.Background = System.Windows.Media.Brushes.LightBlue;
tb.Foreground = System.Windows.Media.Brushes.Blue;
// bind the text block
System.Windows.Data.Binding textBlockBinding = new System.Windows.Data.Binding("TooltipText");
textBlockBinding.Mode = System.Windows.Data.BindingMode.OneWay;
textBlockBinding.Source = bed;
tb.SetBinding(System.Windows.Controls.TextBlock.TextProperty, textBlockBinding);
wp.Children.Add(brdr);
wp.Children.Add(tb);
bedTooltip = new System.Windows.Controls.ToolTip();
bedTooltip.Content = wp;
Any idea why this doesn't work? Maybe I need to use a tooltip object for each control instead of a single global one as a workaround?

The bindings specify a Source, because of that they no longer "care" about the DataContext and hence the bindings do not update if anything other than the property itself on the source-object changes.

Related

MahApps:Metro SplitButton Control Template

Default SplitButton works ok, but when I try to work with it's control template issues arise.
If I try to get Control Template with reflection (with ConstructorInfo) I get empty Control Template for SplitButton. If I try to 'Edit Template copy' in XAML Designer, I get copy which does not work (like ItemsSource does not bind to elements in ListBox of SplitButton as it is always empty).
My version of MahApps Metro is 1.4.3.0
Here is how I try to get Control Template of SplitButton:
MahApps.Metro.Controls.SplitButton ctl = sender as MahApps.Metro.Controls.SplitButton;
Type type = ctl.GetType();
if (type == null)
return;
// Instantiate the type.
ConstructorInfo info = type.GetConstructor(System.Type.EmptyTypes);
Control control = (Control)info.Invoke(null);
// Get the template.
ControlTemplate template = control.Template;
// Get the XAML for the template.
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
StringBuilder sb = new StringBuilder();
XmlWriter writer = XmlWriter.Create(sb, settings);
XamlWriter.Save(template, writer);
The default ControlTemplate is available on GitHub: https://github.com/MahApps/MahApps.Metro/blob/336f7dfc4bda2d0eba8aa270737ca3c11d45128c/src/MahApps.Metro/MahApps.Metro/Themes/SplitButton.xaml
MahApps.Metro is open source so you can download the source code if you want to.
After updating MahApps Metro to 1.5.0 SplitButton works fine with provided Control Templates...

In WPF how do I bind a Content Property of the UserControl to the internal control

I Have a user Control which Contains a ScrollPanel. And I want to bind the userControl's content property to the ScrollPanel.
So my xaml would look like:
<CustomControl>
<StackPanel/>
</CustomControl>
and in my UserControl my ScrollPanel child is set to StackPanel.
Do you mean ScrollViewer?
You have to remove the content from the user control (so the content no longer has a visual parent), then reassign the content to the scroll viewer.
In code:
var scrollViewer = new ScrollViewer();
var content = userControl.Content;
userControl.Content = null; // removes content from visual tree
scrollViewer.Content = content; // reassign content
If there's a way to do this via a binding, I haven't figured it out yet, though the situation where I'm having to do this is slightly different from yours.

Silverlight Image Data Binding

I am new to Silverlight, and have an issue with binding.
I have a class ItemsManager, that has inside its scope another class Item.
class ItemsManager
{
...
class Item : INotifyPropertyChanged
{
...
private BitmapImage bitmapSource;
public BitmapImage BitmapSource
{
get { return bitmapSource; }
set
{
bitmapSource = value;
if(PropertyChanged != null )PropertyChanged("BitmapSource")
}
}
}
}
I do the following in code to test binding:
{
ItemsManager.Instance.AddItem("123");
//Items manager started downloading item visual
//part (in my case bitmap image png)
Binding b = new Binding("Source");
b.Source = ItemsManager.Instance.GetItem("123").BitmapSource;
b.BindsDirectlyToSource = true;
Image img = new Image();
img.SetBinding(Image.SourceProperty, b);
img.Width = (double)100.0;
img.Height = (double)100.0;
LayoutRoot.Children.Add(img);
}
Once image is loaded, image doesn't appear. Though, if I set directly after image has been loaded its source, it displays well.
I also noticed that PropertyChanged("BitmapSource") never fires, because PropertyChanged is null, like Image never binded to it.
I am looking forward to hearing from you!
PropertyChanged("BitmapSource") fires in case of two-way binding.
I.e. if you use two way binding and then manually change image source, like
img.Source = new BitmapImage(new Uri("http://...."));
the property changed event would fire.
As for the image appearance, it seems you bind data in wrong way.
Try declarative bindings.
There are several things wrong with this code:-
You've bound directly to the BitmapImage exposed by the BitmapSource property so you've taken your nested Item class out of the picture anyway.
Also for the property setter to be called you would need something to assign a value to the Image elements Source property and your binding would need to be in two way mode.
Your binding object creation is confused, it specifies a path (which is wrong anyway) but then binds direct to source.
Hence your code would need to look like this:-
Binding b = new Binding("BitmapSource");
b.Source = ItemsManager.Instance.GetItem("123");
b.Mode = BindingMode.TwoWay;
Now when a new BitmapImage is assigned the Image Source property your setter code should run. However it should be born in mind that the this property is of the more general type ImageSource. Hence this code will break if another derivative of ImageSource is assigned instead.
One other thing which may be a problem, I can't recall of the top of my head whether Silverlight supports binding to nested types. You might need to bring your Item class out of ItemsManager and give it a more specific name like ManagedItem.
First, do not bind to BitmapImage unless you have a good reason. Binding to a string is good enough. The implicit conversion will happen automatically. Second, use declaritive binding. Programatic creation and binding is a real mess. Third, only implement INotifyPropertyChanged if you need to send changes of that property to the UI.
You are likely over complicating your situation with all this extra code.
Thanks for explanations. However, I don't need a TwoWay binding. Just one way, once ItemsManager downloads Item image, it should be automatically updated in the Image control.
So, I changed my code to this:
ItemsManager.Instance.AddItem("123");
Binding b = new Binding("BitmapSource");
b.Source = ItemsManager.Instance.GetItem("123");
Image img = new Image();
img.SetBinding(Image.SourceProperty, b);
img.Width = (double)100.0;
img.Height = (double)100.0;
LayoutRoot.Children.Add(img);
I also took Item out of ItemsManager scope, so it is now in its own class file, but image still stays empty, even though bitmap image arrives, and changes in BitmapSource property of Item object.

Combobox background color while not enabled

I have a combobox that I have Enabled = false. When that is the case it causes it to shade to a grey. I was wondering if there was a way I could keep the checkbox background color as cornsilk while it is not Enabled?
The situation is that I have a form that I will refresh with data when an item is selected. If the user selects to edit the record I enable the form to accept changes and since it is mainly textboxes I just change the readonly property of those. But the combobox looks different so I want to see what I can do to make it stay the same like the rest of the form...
Any ideas?
I would simply hide it with a TextBox over it and setting its Visible property to false. Then, you your user click the Edit button, you hide your TextBox and show your ComboBox with its Visible property set to true.
Perhaps you wish to update your TextBox.Text property by setting its value to the ComboBox.SelectedItem property value on the SelectedItemChanged() event handler.
Let's suppose the following:
ComboBox cb = new ComboBox();
// Position, size and other properties are set through design.
cb.SelectedIndex = 0; // Forces selection of first item for demo purposes.
TextBox tb = new TextBox();
tb.Size = cb.Size;
tb.Position = cb.Position;
tb.Text = cb.SelectedItem.ToString();
tb.Visible = true;
tb.Readonly = true;
cb.Visible = false;
Then, clicking the Edit button:
private void EditButton_Click(...) {
tb.Visible = false;
cb.Visible = true;
}
And make your TextBox.Text property value follow your SelectedItem:
private void ComboBox_SelectedIndexChanged(...) {
tb.Text = cb.SelectedItem.ToString;
}
And you would only do the reverse of your EditButton_Click() event handler to bring back your form in read-only mode.
You may consider using Jquery UI or other plugins if aesthetics of form are important. You can control entire look and feel with the CSS.
Hiding combobox with textbox is a possibility as suggested by Will but then you will have to use absolute width for the dropdown.

How to dynamically embed a XAML/Codebehind pair as a child of a StackPanel?

I have a StackPanel called "MainContent".
I can dynamically fill it with UIElements like this:
TextBlock textBlock = new TextBlock();
textBlock.Text = String.Format("This is text");
textBlock.Background = new SolidColorBrush(Colors.Beige);
MainContent.Children.Add(textBlock);
Button button = new Button();
button.Content = "This is a button";
MainContent.Children.Add(button);
But I want to go beyond that and fill it with a XAML/Codebehind pair (e.g. Page or Window):
Type type = this.GetType();
Assembly assembly = type.Assembly;
Window window = (Window)assembly.CreateInstance(String.Format("{0}.{1}", type.Namespace, "Test1"));
MainContent.Children.Add(window);
But the above code complains that I can't add a "Window to a Visual". I can do window.ShowDialog() of course but then it is external to my main window.
I want the Test1 window to be embedded in my application.
How can I do this?
Added: The main question is: how can I get Window (or Page) to act as a UIElement so I can embed them dynamically in StackPanels, etc. Currently looking at XamlLoader, anyone experienced with that?
The easiest way is to have your XAML/code behind inherit from UserControl - and then everything will just work
I don't know if it's possible. But instead of filling the panel with a Window, why not fill it with the root element of the window instead. You'll get all the content and value of the window without the unneeded chrome.
Update: you can grab the root element of a window via the Dependency Property Content
Window w;
object rootElement = w.Content;
Have you tried this?
MainContent.Children.Add(window.Content);

Resources