How do I bring an item to the front in wpf? - wpf

I simply have two grid on top of one another. Given one state of the world, I want grid A to be on top, given another state of the world, I want grid B to be on top. In the old days we could just call grid.BringToFront(), but that doesn't exist anymore, and I can't figure out any way to make that happen.
The best I can figure, I need to create my own custom classes to allow this functionality, but that seems like major overkill for something that used to be so simple.

You can use the Panel.ZIndex property to change the display order of elements in a panel

You have to use the Z index property, and because there are no built-in function to do what you want, I made my own.
The higher the Z value, the 'closer' to front the control is.
So you want to put your control on top without having to set an arbitrary high Z value.
So here is a small function I wrote for myself to do exactly that.
Note: this assume that you are using a Canvas and UserControls.
So you might need to adapt it a little bit if that's not your case.
Basically it will get the index of the control to move, then any control currently above it will go down by 1 and the control to move will be put on top (to maintain hierarchy).
static public void BringToFront(Canvas pParent, UserControl pToMove)
{
try
{
int currentIndex = Canvas.GetZIndex(pToMove);
int zIndex = 0;
int maxZ = 0;
UserControl child;
for (int i = 0; i < pParent.Children.Count; i++)
{
if (pParent.Children[i] is UserControl &&
pParent.Children[i] != pToMove)
{
child = pParent.Children[i] as UserControl;
zIndex = Canvas.GetZIndex(child);
maxZ = Math.Max(maxZ, zIndex);
if (zIndex > currentIndex)
{
Canvas.SetZIndex(child, zIndex - 1);
}
}
}
Canvas.SetZIndex(pToMove, maxZ);
}
catch (Exception ex)
{
}
}

To whom it may concern:
ZIndex property is 0 by default, so if you have (like me) a Canvas with more than 1 element (>4000 Shapes in my case), all will have ZIndex = 0, so changing the ZIndexes with this method will have no effect.
For this to work, I set the ZIndexes to a known value after creating all the elements, so they can be ordered after.
int zIndex = 0;
for (int i = 0; i < canvas.Children.Count; i++) {
UIElement child = canvas.Children[i] as UIElement;
if (canvas.Children[i] is UIElement) Canvas.SetZIndex(child, zIndex++);
}

Instead of stacking the two grids, change the visibility properties so the grid you aren't using is collapsed.

Expanding on the answer from #PicMickael, this will do exactly as they described but with less instructions:
public void BringToFront<T>(T uiElement, Canvas canvas)
{
try
{
foreach (UIElement s in canvas.Children)
{
Canvas.SetZIndex(s, 1);
}
Canvas.SetZIndex(uiElement as UIElement, 2);
}
catch (Exception ex)
{
WriteLog.Error(ex);
}
}

Related

If else condition on radio buttons

I was able to create buttons.I will be using about 65 buttons, how do you use if else condition on the buttons? Can someone please show me an example? Thank you in advance.
private void createButtons()
{
flowLayoutPanel1.Controls.Clear();
for(int i = 0;i <10;i++)
{
RadioButton b = new RadioButton();
b.Name = i.ToString();
b.Text = "radiobutton" + i.ToString();
b.AutoSize = true;
flowLayoutPanel1.Controls.Add(b);
}
}
How about putting the RadioButtons in a list or an array? This way you could use if (allRadioButtons[1].checked) {...}.
Here is an example
private List<RadioButton> allRadioButtons = new List<RadioButton>();
private void createButtons()
{
flowLayoutPanel1.Controls.Clear();
for (int i = 0; i < 10; i++)
{
RadioButton b = new RadioButton();
b.Name = i.ToString();
b.Text = "radiobutton" + i.ToString();
b.AutoSize = true;
flowLayoutPanel1.Controls.Add(b);
//add every button to the list
//the one with the Text radiobutton0 will be accessible as allRadioButtons[0]
//the one with the Text radiobutton1: allRadioButtons[1]
//etc
allRadioButtons.Add(b);
}
}
//you can use your list in any other method
private void myMethod() {
if (allRadioButtons[0].Checked)
{
//do something
}
}
If Andrea's answer didn't work for you (since you didn't mark it as a solution), another option would be to create a container, such as a GroupBox, and then add your programatically created RadioButton controls to this container. Then you can loop over the controls belonging to the GroupBox like this:
foreach (Control c in myGroupBox.Controls)
{
if ((RadioButton)c).Checked)
//Do something
}
This will loop over all the controls in the GroupBox and cast them to a RadioButton and check if they're checked or not. I've used something similar to this as the basis for quite a few requirements in different applications; it's very easy to make a recursive method that takes a ControlCollection, loop over it and apply logic as needed depending on some condition, like the type of control or perhaps the Tag value of the control.
Then as far as adding the RadioButtons to the GroupBox at run time you just do something like myGroupBox.Controls.Add(b) where b is the RadioButton you've created in your for loop in your sample code. More on runtime control creation here.

Custom panel layout doesn't work as expected when animating (WPF)

I've got a custom (and getting complex) TabControl. It's a gathering of many sources, plus my own wanted features. In it is a custom Panel to show the headers of the TabControl. Its features are to compress the size of the TabItems until they reached their minimum, and then activates scrolling features (in the Panel, again). There is also another custom panel to hold a single button, that renders on the right of the TabItems (it's a "new tab" button).
It all works great, until I try to animate the scrolling.
Here are some relevant snippets :
In the CustomTabPanel (C#, overriding Panel and implementing IScrollInfo):
private readonly TranslateTransform _translateTransform = new TranslateTransform();
public void LineLeft()
{
FirstVisibleIndex++;
var offset = HorizontalOffset + _childRects[0].Width;
if (offset < 0 || _viewPort.Width >= _extent.Width)
offset = 0;
else
{
if (offset + _viewPort.Width > _extent.Width)
offset = _extent.Width - _viewPort.Width;
}
_offset.X = offset;
if (_scrollOwner != null)
_scrollOwner.InvalidateScrollInfo();
//Animate the new offset
var aScrollAnimation = new DoubleAnimation(_translateTransform.X, -offset,
new Duration(this.AnimationTimeSpan), FillBehavior.HoldEnd) { AccelerationRatio = 0.5, DecelerationRatio = 0.5 };
aScrollAnimation.Completed += ScrollAnimationCompleted;
_translateTransform.BeginAnimation(TranslateTransform.XProperty, aScrollAnimation , HandoffBehavior.SnapshotAndReplace);
//End of animation
// These lines are the only ones needed if we remove the animation
//_translateTransform.X = -offset;
//InvalidateMeasure();
}
void ScrollAnimationCompleted(object sender, EventArgs e)
{
InvalidateMeasure();
}
the _translateTransform is initialized in the constructor :
base.RenderTransform = _translateTransform;
Again, everything is fine if I remove the animation part and just replace it with the commented out lines at the end.
I must also point out that the problem is NOT with the animation itself. That part works out well. The problem is about when I remove some tab items : all the layout then screws up. The TranslateTransformation seems to hold on some wrong value, or something.
Thanks in advance.
Well. As it's often the case, I kept working on the thing, and... answered myself.
Could still be useful for other people, so here was the catch. In the line :
var aScrollAnimation = new DoubleAnimation(_translateTransform.X, -offset, new Duration(this.AnimationTimeSpan), FillBehavior.HoldEnd)
{ AccelerationRatio = 0.5, DecelerationRatio = 0.5 };
the FillBehavior should have been FillBehavior.Stop.
As easy as that!

Cant see controls inside User Control in the VisualTreeHelper

I have UserControl in wpf 4.0 which contains buttons , labels , textboxes etc....
I want to loop those controls and when I get a buuton , I want to take it's name and save it to my list . Basically , all I want to do is to create a Names_list of all my buttons in the UserControl.
I have a method that iterates all the controls and if it finds a button , it saves it's name -
public void EnumVisual(Visual myVisual)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(myVisual); i++)
{
// Retrieve child visual at specified index value.
Visual childVisual = (Visual)VisualTreeHelper.GetChild(myVisual, i);
Button _button = childVisual as Button;
if (_button != null)
{
Class_Button _newButtonClass = new Class_Button();
if (_button.Name != null)
{
_newButtonClass.ButtonName = _button.Name;
}
ButtonsList.Add(_newButtonClass);
}
// Enumerate children of the child visual object.
EnumVisual(childVisual);
}
}
I always get an empty list.
When I enter in to the code by debugging it and I watch the VisualTree of my UserControl , I see all the Panels and GroupBoxes and Grids but I dont see buttons , labels and texboxes although every control has a x:Name and every control is x:FieldModifier="public". This is very odd....And I cant understand the reason for that as well as how to solve this problem...
can anyone tell what I am doing wrong?
thanks
As suggested by #GazTheDestroyer you want to make sure the control template has been applied before trying to use VisualTreeHelper. Try:
public void EnumVisual(Visual myVisual)
{
if(myVisual is FrameworkElement)
((FrameworkElement)myVisual).ApplyTemplate();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(myVisual); i++)
{
// Retrieve child visual at specified index value.
Visual childVisual = (Visual)VisualTreeHelper.GetChild(myVisual, i);
Button _button = childVisual as Button;
if (_button != null)
{
Class_Button _newButtonClass = new Class_Button();
if (_button.Name != null)
{
_newButtonClass.ButtonName = _button.Name;
}
ButtonsList.Add(_newButtonClass);
}
// Enumerate children of the child visual object.
EnumVisual(childVisual);
}
}
You can use a tool like Snoop
or WPF Inspector
to examine the visual tree of your control.
If these tools are able to do so, the error must be somewhere in your code, right?

How do you use MeasureOverride in Silverlight?

I think I have understood how MeasureOverrride works, but I am trying to use it in a very simple case and It doesn't work... So now I'm not so sure... After using Measureoverride do I have to use Arrageoverride too or the system will do it for me? The situation is this one: I have a LinearLayout class inherited from Panel and it has two fields called wrapwidht and wrapheigh, if they are true the width or the height of the LinearLayout has to be as its children require. so my Measureoveride looks like:
protected override Size MeasureOverride(Size availableSize) {
Size panelDesiredSize = new Size();
if ((this.widthWrap) || (this.heightWrap))
{
foreach (UIElement elemento in this.Children)
{
System.Diagnostics.Debug.WriteLine(((FrameworkElement)elemento).DesiredSize.ToString());
if (this.Orientation.Equals(System.Windows.Controls.Orientation.Vertical))
{
if (this.widthWrap)
{
//the widest element will determine containers width
if (panelDesiredSize.Width < ((FrameworkElement)elemento).Width)
panelDesiredSize.Width = ((FrameworkElement)elemento).Width;
}
//the height of the Layout is determine by the sum of all the elment that it cointains
if (this.heightWrap)
panelDesiredSize.Height += ((FrameworkElement)elemento).Height;
}
else
{
if (this.heightWrap)
{
//The highest will determine the height of the Layout
if (panelDesiredSize.Height < ((FrameworkElement)elemento).Height)
panelDesiredSize.Height = ((FrameworkElement)elemento).Height;
}
//The width of the container is the sum of all the elements widths
if (this.widthWrap)
panelDesiredSize.Width += ((FrameworkElement)elemento).Width;
}
}
}
System.Diagnostics.Debug.WriteLine("desiredsizzeeeeeee" + panelDesiredSize);
return panelDesiredSize;
}
The children I am aading to the LinerLayout are 3 buttons, but nothing is drawn.. even if the panelDesiredSize filed is correct.. so maybe I didn't understand how it works very well. If anybody can help me would be very nice :-)
Check my answer on a previous post similar to yours: Two Pass Layout system in WPF and Silverlight
The answer is that no, you don't have to override ArrangeOverride, but what is the point of using MeasureOverride if you are not going to use ArrangeOverride?
You should call the Measure method on the child. See the example here: http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.measureoverride.aspx
You must call Measure on "elemento". It's during the Measure that Silverlight creates the UI elements declared in the elemento's template since they'll be needed to actually measure and come up with a desired size. You should then use the elemento.DesiredSize.Width and Height to come up with the desired size for your panelDesiredSize.

Selecting an object on a WPF Canvas?

I have a WPF Canvas with some Ellipse objects on it (displayed as circles). Each circle is from a collection class instance which is actually a custom hole pattern class. Each pattern has a certain number of circles, and each circle then gets added to the canvas using an iteration over the collection using the code below.
So, the canvas is populated with a bunch of circles and each circle belongs to a certain pattern instance. You can see a screenshot here: http://twitpic.com/1f2ci/full
Now I want to add the ability to click on a circle on the canvas, and be able to determine the collection it belongs to, so that I can then do some more work on the selected pattern to which that circle belongs.
public void DrawHoles()
{
// Iterate over each HolePattern in the HolePatterns collection...
foreach (HolePattern HolePattern in HolePatterns)
{
// Now iterate over each Hole in the HoleList of the current HolePattern...
// This code adds the HoleEntity, HoleDecorator, and HoleLabel to the canvas
foreach (Hole Hole in HolePattern.HoleList)
{
Hole.CanvasX = SketchX0 + (Hole.AbsX * _ZoomScale);
Hole.CanvasY = SketchY0 - (Hole.AbsY * _ZoomScale);
canvas1.Children.Add(Hole.HoleEntity);
}
}
}
All FrameworkElements have a Tag property which is of type object that can be used to hold arbitrary information. You could assign the HolePattern to the Tag property and easily use that later to get the associated collection.
i.e.:
...
Hole.HoleEntity.Tag = HolePattern as object;
canvas1.Children.Add(Hole.HoleEntity);
later on in the click event:
event(object sender,....)
{
Ellipse e = sender as Ellipse;
HolePattern hp = e.Tag as HolePattern;
...
}
So you probably already read my reply where I said I had it working. And it does work perfectly, (except that it requires great precision with the mouse), but I want to ask this: is it really smart to add an event handler to EVERY ellipse that gets added to a canvas? Now I don't know what kind of memory bog that could be, or maybe it is a piece of cake for WPF and Windows to handle.
In a practical case, I guess there would be not more that 30-50 holes even on a screen that had multiple patterns, but still; FIFTY event handlers? It just seems scary. And actually, each "Hole" is visually represented by two concentric circles and a text label (see the screenshow here: http://twitpic.com/1f2ci/full ), and I know the user would expect to be able to click on any one of those elements to select a hole. That means an event handler on 3 elements for every hole. Now we could be talking about 100 or more event handlers.
It seems like there should be a solution where you could have just one event handler on the Canvas and read the element reference under the mouse, then work off of that to get the .Tag property of that elment, and so on.
I thought I'd post my final and more refined solution in case it helps anyone else.
void canvas1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
int ClickMargin = 2;// Adjust here as desired. Span is in both directions of selected point.
var ClickMarginPointList = new Collection<Point>();
Point ClickedPoint = e.GetPosition(canvas1);
Point ClickMarginPoint=new Point();
for (int x = -1 * ClickMargin; x <= ClickMargin; x++)
{
for (int y = -1 * ClickMargin; y <= ClickMargin; y++)
{
ClickMarginPoint.X = ClickedPoint.X + x;
ClickMarginPoint.Y = ClickedPoint.Y + y;
ClickMarginPointList.Add(ClickMarginPoint);
}
}
foreach (Point p in ClickMarginPointList)
{
HitTestResult SelectedCanvasItem = System.Windows.Media.VisualTreeHelper.HitTest(canvas1, p);
if (SelectedCanvasItem.VisualHit.GetType().BaseType == typeof(Shape))
{
var SelectedShapeTag = SelectedCanvasItem.VisualHit.GetValue(Shape.TagProperty);
if (SelectedShapeTag!=null && SelectedShapeTag.GetType().BaseType == typeof(Hole))
{
Hole SelectedHole = (Hole)SelectedShapeTag;
SetActivePattern(SelectedHole.ParentPattern);
SelectedHole.ParentPattern.CurrentHole = SelectedHole;
return; //Get out, we're done.
}
}
}
}

Resources