Reset control's parent in WPF - wpf

If I add a control to a canvas and than removes it, I can not re-add it to the same canvas (or to any other canvas for that matter) any idea how can I reset the parent?
mainCanvas.Children.Add(item);
mainCanvas.Children.Remove(item);
mainCanvas.Children.Add(item); // Will throw an exception that parent was already set.
Thanks,
Eden.

Are you sure there is not something else going on in your code?
I just tried a new wpf application with :
public MainWindow()
{
InitializeComponent();
Button b = new Button();
b.Content = "hello";
Canvas c = new Canvas();
c.Children.Add(b);
c.Children.Remove(b);
c.Children.Add(b);
Content = c;
}
and it worked fine. Have you got any collection changed delegates?

Related

trouble displaying flowdocument

I'm having some problems displaying the contents of a flowdocument in a flowdocumentscrollviewer. I create a generic list that holds a class which contains an int, string and a flowdocument.
In a WPF listbox, I am trying to display the flowdocument in the scrollviewer alongside a button. I use the following function called from the WPF window constructor to populate the listbox
private void populateListBox()
{
foreach(Element el in _notesList)
{
StackPanel sp = new StackPanel();
sp.Orientation = Orientation.Horizontal;
Button b = new Button();
b.Content = el._theID;
sp.Children.Add(b);
FlowDocumentScrollViewer fdsv = new FlowDocumentScrollViewer();
fdsv.MinWidth = 400;
fdsv.Document = el._theDoc;
sp.Children.Add(fdsv);
ListBoxItem lbi = new ListBoxItem();
lbi.Content = sp;
noteList.Items.Add(lbi);
}
}
But the code does not work. There are no errors but the scrollviewers are just blank in the listbox. I also tried storing the classes in an ObservableList and binding to the Document property but that didn't work either.
Any ideas what is happening?
Nevermind. I figured it out.
Further down in the program execution I was copying the flowdocument blocks to a merged document in a foreach statement. This doesn't work even if you use Blocks.ToList(). I eventually found a way to copy the document contents to another document here.

Wpf Adorner not responding to interactions

I'm trying to create an overlay in wpf (with darkening background), similar to the ones you can find on the web to popup images.
I would like it to be reusable in more than 1 part of the application, with diffent types of content.
this is the temporary code of the constructor of the adorner class (just to try)
private readonly Grid _grid = new Grid();
public DarkOverlayAdorner(UIElement adornedElement, Object content) :
base(adornedElement)
{
_grid.Background = new SolidColorBrush(Color.FromArgb(99, 0, 0, 0));
IsHitTestVisible = true;
var visual = content as UIElement;
if (visual != null)
_grid.Children.Add(visual);
}
In addition in the class (of course), I have the ovverrides of MeasureOverride and ArrangeOverride to give the adorner the correct size of the adorned element, GetVisualChild, and VisualChildCount...
The problem here is that the adorner is correctly shown, but no events or behaviour are applied on the adorned element. For example:
AdornerLayer layer = AdornerLayer.GetAdornerLayer(textBoxProva);
layer.Add(new DarkOverlayAdorner(textBoxProva, new Button{Content = "prova"}));
The button here is shown, but I can-t click the button and no effects on button mouseover are applied.
I still can't figure out the problem.
Ok, I've lost a lot of time trying to figure out what was the problem.
In the end I found the solution:
If you want the element added to react to events, I think that the element must be bound to the visual tree of the adorner.
The way to do it is to use a VisualCollection, intitialized to the adorner itself:
VisualCollection visualChildren;
FrameworkElement #object;
public DarkOverlayAdorner(UIElement adornedElement) :
base(adornedElement)
{
visualChildren = new VisualCollection(this);
#object = new Button {Content = "prova"};
visualChildren.Add(#object);
}
protected override Visual GetVisualChild(int index)
{
return visualChildren[index];
}
This way the events are correctly routed.
You might want to take a look at the ChildWindow control in the Extended WPF Toolkit. It is a control that pops up a Window with a modal background effect, and you can specify the content to put inside the Window.

How to show a loading graphic/animation when wpf data binding is taking place

I have a WPF user control that contains a DataGrid. I'm binding an ObservableCollection of view models to it. Each view model has another collection of view models that I'm using to bind another DataGrid to. So the effect is a DataGrid with a nested DataGrid contained in the row details template.
Normally the binding is quite quick, but sometimes when there's a lot of data it can hang the UI while the binding/drawing is taking place.
Is there a way where I can either show a loading animation or progress bar while the binding/drawing is in progress?
There's probably a more formal, or at least simpler solution, but you could use a modal popup window that is shown in a worker thread and is closed asynchronously when your is grid done loading:
Window waitWindow = new Window { Height = 100, Width = 200, WindowStartupLocation = WindowStartupLocation.CenterScreen, WindowStyle = WindowStyle.None };
waitWindow.Content = new TextBlock { Text = "Please Wait", FontSize = 30, FontWeight = FontWeights.Bold, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center };
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate
{
Dispatcher.BeginInvoke(new Action(delegate { waitWindow.ShowDialog(); }));
DataLoader dataLoader = new DataLoader(); // I made this class up
dataLoader.DataLoaded += delegate
{
Dispatcher.BeginInvoke(new Action(delegate() { waitWindow.Close(); }));
};
dataLoader.LoadData();
};
worker.RunWorkerAsync();
You can replace the TextBlock with something pretty like a loading bar, and you could make the code re-usable by parameterizing the object that handles the loading of the grid(s) and passing it in to a commonly used method.
I hope that works for you.
I had the same problem and this is how I solved it.
I discovered that DataGrid will only start creating controls when it displays the grid. In my case this was the time consuming process. After some tracing I found that creating the controls happens during measuring !
My solution is to override MeasureOverride and put the wait cursor around the base class call. I encapsulated my wait cursor setting in a class. So the code looks like this.
protected override Size MeasureOverride(Size availableSize)
{
using (new DisposableWaitCursor(this))
{
return base.MeasureOverride(availableSize);
}
}

WPF Tooltip binding not updating

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.

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