How to find the Parent of a CustomControl from inside the CustomControl - c#

I have a class which extends Canvas in WPF.
This class is placed in a ScrollViewer.
Without passing a specific reference to the ScrollViewer into the Canvas, I want to find the ScrollViewer which contains the Canvas from within the Canvas itself.
The Parent property of the class which extends Canvas is null, and every attempt to use the VisualTreeHelper just returns null as well.
I have attempted to find the visual ancestor using VisualTreeHelper.GetParent(this), however the parent property is null.
As the ExtendedCanvas will be used in multiple instances, I would like it to be able to resolve its containing ScrollViewer without the need to specifically reference the ScrollViewer in either code behind or in XAML.
I realise that I could add a dependency propery in the ExtendedCanvas and create a binding in the XAML, however I would like the component to work by simply dropping it into a container.
Similarly, I would not be averse to placing the ScrollViewer onto a panel of some sort, then placing my ExtendedCanvas within it, so that my component uses that panel as its lowermost containing element.
What is puzzling me is that as I understand it, the VisualTreeHelper will navigate the entire visual tree for the running application. It seems that either my assumption is entirely wrong, or it can only navigate downwards from the specified component.
Is this possible to achieve without the above approaches?
Example Code:
cs -
public class ExtendedCanvas:Canvas {
//I wish to automatically populate this scroll viewer
//reference to the instance of the scrollviwer which contains
//this ExtendedCanvas instance
private ScrollViewer _containingScrollViewer = null;
}
xaml -
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" >
<local:ExtendedCanvas x:Name="extendedCanvas" />
</ScrollViewer>

You can find its parent like this:
public ExtendedCanvas()
{
//it hasn't been added to its parent yet
Loaded += ExtendedCanvas_Loaded;
}
private void ExtendedCanvas_Loaded(object sender, RoutedEventArgs e)
{
//now it is added to its parent
_containingScrollViewer = Parent as ScrollViewer;
}

Related

Drag/drop controls onto child Canvas without parent Canvas being notified

I'm trying to create a visual "designer" that will allow users to drag controls from a toolbox onto a design canvas. The excellent tutorial here has helped me get a basic system up and going - I can drag controls from the toolbox, select and resize them etc. Amongst other things,the code uses a modified Canvas control, overriding the OnDrop method.
However, I'd like to give the user the option of defining "panels" within the design: effectively smaller Canvas's containing the toolbox controls - as an example:
So when the user drags the button onto Canvas_1, OnDrop fires and all is good. However, if the user creates Canvas_2, and then drags the button onto Canvas_2 - itself a child control of Canvas_1 - the parent OnDrop still fires and the button is added to Canvas_1.
I've tried setting the ZIndex of Canvas_2 to greater than Canvas_1, to no avail - the parent Canvas always gets the event. How can I ensure that Canvas_2 gets the OnDrop events for controls landing on it? Is there a different approach I should be using?
The event DragDrop.DragEnter uses a bubbling routing strategy, that means the event will travel upwards in the visual tree starting from the source element, giving a chance for all the elements along the route to handle the event.
When you drop something in the inner Canvas, it handles it, but doesn't stop the event propagation, so the parent element (the containing Canvas) will handle it again.
In the overriden OnDrop method you have a DragEventArgs parameter.
Set its Handled property to true if you want to prevent containing elements to handle the same event.
Example
I created a simple derived Canvas as well:
public class MyCanvas : Canvas
{
public static readonly DependencyProperty StopDropPropagationProperty = DependencyProperty.Register(
"StopDropPropagation", typeof (bool), typeof (MyCanvas), new PropertyMetadata(default(bool)));
public bool StopDropPropagation
{
get { return (bool) GetValue(StopDropPropagationProperty); }
set { SetValue(StopDropPropagationProperty, value); }
}
protected override void OnDrop(DragEventArgs e)
{
Children.Add(new TextBlock
{
Text = "Dropped here"
});
e.Handled = StopDropPropagation;
}
}
In the view I've put two of these, one nested inside the other.
<local:MyCanvas AllowDrop="True" Background="DarkGray">
<local:MyCanvas Width="300" Height="300" Canvas.Left="100" Canvas.Top="10" Background="BurlyWood"
StopDropPropagation="True"></local:MyCanvas>
</local:MyCanvas>
The thing to note is that I added the new DependencyProperty called StopDropPropagation and set it to true in the inner Canvas. That will set the Handled to true.
So when I drop something in the inner Canvas the TextBlock will only be added in that element:

How to create White CustomUIItem with DockPanel root?

I have custom controls set in my window with a complicated structure.
I'd like to create a custom control to incapsulate all logic of this control (with inner grids, buttons, etc.).
In a XAML i've added:
<DockPanel AutomationProperties.AutomationId="WidgetName">
In a source code i've added:
[ControlTypeMapping(CustomUIItemType.Custom)]
public class MyWidget : CustomUIItem{}
Add now i'm trying to find the item:
_window.Get<MyWidget>("WidgetName");
It throws with error that couldn't find control with Custom type and 'WidgetName' name.
Also there will be a set of such controls in a window. Is there something like
_window.GetAll<> instead of .Get<>?
Try to use Window.Get(SearchCriteria.All);

Adding child components to parent's panel?

m developing a sort of project which I needed a cool customizable interface, so I designed a 'parent-form' from which all childs would get 'stylized', according to XML customization options.
I added a TableLayoutPanel to draw borders and a Panel in the middle, where child forms would supposedly add their components and make their jobs.
The problem I face is, even though I set that 'content panel' to 'public', the designer wont let me add controls to it from the child forms.
Is there any different way I can make designable forms deriving from a 'customizable' superclass?
Edit: The parent class is public, every container containing the Content-Panel are also set to public.
I manually added to child's designer.cs a new Panel inside the parent's content pane, set it to DockStyle.Fill. When I came back to the Designer, it will now let me add components to child's content Panel.
A bit messy and I'm pretty sure there shall be another way around...
But I'll work along like this until i can figure out a better workaround.
I have added a new public Panel from code other than designer in the parent's class scope, Then in the parent constructor I added it to the TableLayoutPanel, configured docking and colspan from constructor code, below InitializeComponents() call and BAM!
public Panel contentPane = new Panel();
public Dialogo()
{
InitializeComponent();
Content.Controls.Add(contentPane);
contentPane.Dock = DockStyle.Fill;
// More code
}
So its a contentPane inside 'Content' which is another panel in the second line of the table ocupying 5 columns (so the table surrounds it and allows me to draw the borders around.
I don't know why, but having added the content-panel in code other than on the designer allowed me to directly add components to the panel from the Designer in child forms.

How can I get the parent of a FrameworkElement in Windows Store app

I am writing a C# XAML Windows Store app. I have a grid on my page. I add a child element to that grid and I immediately try to get the parent of this child. The parent is null. There does not seem to be a way to get the parent grid from the child element. Here's some code to show what I'm doing. It's not all that complicated:
This is the code that adds the child element and tries to get the parent element:
FrameworkElement FieldView = Field.getView( true );
ContentPanel.Children.Add( FieldView );
Panel theParent = VisualTreeHelper.GetParent(FieldView) as Panel;
FrameworkElement theotherparent = (FrameworkElement)FieldView.Parent;
ContentPanel is a Grid on the page. FieldView is a UserControl. The page displays just fine and there are no exceptions in any of the code. The only thing that is wrong is that I cannot get the parent of FieldView. It is null.
Before posting this, I did a quick test to make sure that UserControl was not the issue. using a TextBlock shows the same problem:
TextBlock Derf = new TextBlock();
PageGroup.ContentPanel.Children.Add(Derf);
Panel theParent2 = VisualTreeHelper.GetParent(Derf) as Panel;
FrameworkElement theotherparent2 = (FrameworkElement)Derf.Parent;
Does the parent property never get set in a Windows Store app? This code worked fine on Window Phone 8.
Thanks for any help.
Dave
As Nate suggested, adding child elements to a Grid works as expected when it is done within the "Loaded" event handler. It only fails within the "OnNavigatedTo" handler. Changing the place in the code where I add the children lets me get a non-null parent.
I would like to point out that this is a bit strange (like a bug). The Grid object exists, so it doesn't make sense that it cannot be set as the parent of the child object. Additionally, even if the parent property is set to null, the code somehow knows that there is a parent object/container because it throws an exception if I try to add the child to more than one Grid.

RibbonGroup causes its children lose their DataContext

I am developing a WPF project and right now I am working on the Ribbon area.
Inside of the Ribbon I have some controls such as RibbonComboBox, RibbonTextBox, etc.
I have several RibbonTab in my Ribbon so I set one view model for each RibbonTab as following:
<rb:Ribbon Name="Ribbon">
<rb:RibbonTab Header="Tab One" Name="tab1">
<rb:RibbonTab.DataContext>
<vm:TabOneViewModel />
</rb:RibbonTab.DataContext>
So, each control within those tabs inherits their respective view model.
When I run the application everything works as expected. The problem starts when I resize the container window to a very small size, after doing this: Every control loses their bindings since their DataContext is replaced by an object called DisconnectedItem.
I have done some research and found that this is due to the controls stop being part of the visual tree and for that reason the binding engine sets their DataContext to DisconnectedItem.
What can I do to prevent my controls from losing their DataContext?
EDIT:
I just found that the main problem comes from the RibbonGroup control which after collapsing causes its visual children lose their DataContext.
I usually add DataContext="{Binding Mode=OneTime}" line for all ribbon controls as a workaround, which fixes the issue completely. You won't be able to change DataContext more than once though.
I just found a workaround for the RibbonGroup control issue here
The solution I took is creating a new control which inherits from RibbonGroup control.
public class MyRibbonGroup : RibbonGroup
{
public MyRibbonGroup()
: base()
{
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
// Force the bindings to be restored after
// the ribbon group collapsed or expanded to a menu button.
if (e.Property == RibbonGroup.IsCollapsedProperty)
{
object objDataContext = this.DataContext;
this.DataContext = null;
this.DataContext = objDataContext;
}
}
}

Categories

Resources