Find all controls in all regions in WPF PRISM application - c#

I am trying to add tooltip to all controls in my WPF application i tried
foreach(IRegion region in _RegionManager.Regions)
{
foreach(IView view in region.Views)
{
foreach(Control c in view.)//here my problem
c.ToolTip = "some tooltip";
}
}
That did not work because i have not any idea where I should looking for controls in that case.
Thank you for any advice.

In WPF, views should be defined in declarative way via XAML markup.
You would normally define your tooltips in your views:
<UIElement ToolTip="toolTipContent"/>
Of course, you can use bindings:
<UIElement ToolTip="{Binding ToolTipText}"/>
Note, that Prism's IView interface doesn't provide any information about the actual type of the view instance. It could be a Page, an UserControl or even a TextBox.
You can, if you want to, use linq and get only those views, which are e.g. UserControls:
foreach(UserControl view in region.Views.OfType<UserControl>())
{
// do something...
}
But that doesn't really help you, since in WPF there's no such easy way to iterate through child elements of an UserControl, as we did it in in Windows Forms.
You could use the VisualTreeHelper and traverse your visual tree looking for the child elements, but that's awkward:
void GetChildControls(IList<Visual> container, Visual parent)
{
int childCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childCount; i++)
{
Visual visual = (Visual)VisualTreeHelper.GetChild(parent, i);
container.Add(visual);
if (VisualTreeHelper.GetChildrenCount(visual) > 0)
{
GetChildControls(container, visual);
}
}
}

Related

Binding treeview to a property of different collections

I'm currently converting a WPF app to a WinForms app, and I'm having some trouble when it comes to a treeview.
I have four nodes which are set up in the designer like so:
this.treeView1.Name = "treeView1";
treeNode1.Name = "shapeNode";
treeNode1.Text = "Shape Files";
treeNode2.Name = "mdbNode";
treeNode2.Text = "MDB Files";
treeNode3.Name = "tiffNode";
treeNode3.Text = "Tiff Files";
treeNode4.Name = "kmlNode";
treeNode4.Text = "KML Files";
this.treeView1.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
treeNode1,
treeNode2,
treeNode3,
treeNode4});
I also have four observable collections that are supposed to correspond with each treeNode, specifically the "Name" property each of these types of objects has:
public ObservableCollection<ShapeFileFeatureLayer> ShapeFileLayers
{
get { return mapModel.ShapeFileLayers; }
set { mapModel.ShapeFileLayers = value; OnPropertyChanged("ShapeFileLayers"); }
}
public ObservableCollection<PersonalGeoDatabaseFeatureLayer> MdbFileLayers
{
get { return mapModel.MdbFileLayers; }
set { mapModel.MdbFileLayers = value; OnPropertyChanged("MdbFileLayers"); }
}
public ObservableCollection<GeoTiffRasterLayer> TiffFileLayers
{
get { return mapModel.TiffFileLayers; }
set { mapModel.TiffFileLayers = value; OnPropertyChanged("TiffFileLayers"); }
}
public ObservableCollection<KmlFeatureLayer> KmlFileLayers
{
get { return mapModel.KmlFileLayers; }
set { mapModel.KmlFileLayers = value; OnPropertyChanged("KmlFileLayers"); }
}
So for each item in one of the collections, it'll populate under the correct node.
Now in WPF, following MVVM, I just add a new TreeViewItem, and bind it's ItemsSource to the correct collection in the viewmodel, and bind the context/text of the child node to "Name" . But I can't do this in winforms. In fact, I don't see a "child" or "items" property for the parent nodes, or any bindable property at all.
Do I really have to add/remove nodes manually to the parent nodes every time the collections change?
Your situation is tough or almost impossible to handle.
But I have another recommendation.
Winforms has capability of WPF interoperability.
You have to use ElementHost control. You can find this control in toolbox under WPF Interoperability.
Just add ElementHost control to your winform. And then add the WPF control to ElementHost.
If you have multiple WPF controls, create WPF user control with all those controls and add the user control to ElementHost.
This makes your job simple with very less code changes.
For a sample look into below link
https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.integration.elementhost?view=netframework-4.7.1

Common class of TextBlock, Grid in WPF C#

I am creating a common animation function for all WPF Controls like TextBlock, Grid, TextBox... like below
private void animateFadeOut(*** displayObj)
{
displayObj.Opacity = 1;
System.Windows.Media.Animation.DoubleAnimation fadingAnimation = new System.Windows.Media.Animation.DoubleAnimation();
fadingAnimation.From = 1;
fadingAnimation.To = 0;
fadingAnimation.Duration = new Duration(TimeSpan.FromSeconds(0.5));
displayObj.BeginAnimation(TextBlock.OpacityProperty, fadingAnimation);
}
So I just want to know that which class should i write in place of ***. I tried UserControl, Object but I got issue in UserControl for Converting TextBlock or Grid to UserControl. And in Object, there is no Opacity value. So what is the best way to handle this?
Highest common ancestor for Grid, TextBlock and TextBox is FrameworkElement but if you want to animate Opacity then it's property of even higher class UIElement
private void animateFadeOut(UIElement displayObj)
{
displayObj.Opacity = 1;
System.Windows.Media.Animation.DoubleAnimation fadingAnimation = new System.Windows.Media.Animation.DoubleAnimation();
fadingAnimation.From = 1;
fadingAnimation.To = 0;
fadingAnimation.Duration = new Duration(TimeSpan.FromSeconds(0.5));
displayObj.BeginAnimation(UIElement.OpacityProperty, fadingAnimation);
}
Though not sure if your code would work for Grid also, but the class you are looking for is "UIElement"
You can check their Inheritance Hierarchy on MSDN.
E.g. for Grid:
Inheritance Hierarchy
System.Object
System.Windows.Threading.DispatcherObject
System.Windows.DependencyObject
System.Windows.Media.Visual
System.Windows.UIElement
System.Windows.FrameworkElement
System.Windows.Controls.Panel
System.Windows.Controls.Grid
System.Windows.Controls.Primitives.SelectiveScrollingGrid
They all under System.Windows.Controls namespace, but I found out that use FrameworkElement is better (as in my case I need to invoke BringIntoView()). You could check the hierarchy and decide by yourself. But AFAIK, FrameworkElement should be used as it provides everything (property and method) you may need to get access to in your situation.

Can I give controls an index property in C# like in VB?

I've found similar answers to my question before, but not quite to what I'm trying to do...
In Visual Basic (last I used it, in 06/07) there was an "Index" property you could assign to multiple controls with the same name. I used this primarily to loop through controls, i.e.:
For i = 1 to 500
picSeat(i).Print "Hello"
Next i
Is there a way to do this in C#? I know there is a .IndexOf(), but would that really help for what I'm doing? I want to have multiple controls with the same name, just different index.
This is a Windows Form Application, and I'm using Visual Studio 2012. I am talking about controls, not arrays/lists; this was possible in VB and I was wondering if it was possible at all in C#. So I want to have, say, 30 seats in a theatre. I want to have each seat represented by a picturebox named "picSeat". VB would let me name several objects the exact same, and would assign a value to a control property "Index". That way, I could use the above loop to print "Hello" in every picture box with only 3 lines of code.
No, this feature does not exist in C#, and was never implemented in the transition from classic VB to VB.Net.
What I normally do instead is put each of the controls in question in a common parent container. The Form itself can work, but if you need to distinguish these from others of the same type a GroupBox or Panel control will work, too. Then, you access the controls like this:
foreach (var picBox in parentControl.Controls.OfType<PictureBox>())
{
// do something with each picturebox
}
If you want to use a specific control, just write by name:
pictureBox6.SomeProperty = someValue;
If you need to change a specific control determined at run-time, normally this is in response to a user event:
void PictureBox_Click(object sender, EventArgs e)
{
var picBox = sender As PictureBox;
if (picBox == null) return;
//picBox is now whichever box was clicked
// (assuming you set all your pictureboxes to use this handler)
}
If you really really want the Control Arrays feature, you can do it by adding code to create the array to your form's Load event:
PictureBox[] pictureBoxes = Me.Controls.OfType<PictureBox>().ToArray();
Are we talking WinForms here? I'm not sure, but I don't think you can have multiple controls in winforms with same name. But I vaguely recall doing something similar and the solution was to name them Button_1, Button_2 etc. Then you can iterate through all controls and get your own index.
Beware though that if you want to instanciate a separate control for each seat in a theatre, you might run into some serious performance issues :) I've done something similar to that as well and ended up drawing the whole thing on a canvas and using mouse coordinates to handle the events correctly.
You may want to check out the Uid property of controls.
(http://msdn.microsoft.com/en-us/library/system.windows.uielement.uid(v=vs.110).aspx)
You can access Control through Uid property with the following
private static UIElement FindUid(this DependencyObject parent, string uid)
{
var count = VisualTreeHelper.GetChildrenCount(parent);
if (count == 0) return null;
for (int i = 0; i < count; i++)
{
var el = VisualTreeHelper.GetChild(parent, i) as UIElement;
if (el == null) continue;
if (el.Uid == uid) return el;
el = el.FindUid(uid);
if (el != null) return el;
}
return null;
}
And simply use
var control = FindUid("someUid");
I copied code from this post
If you create an indexed dictionary of your user control, it will behave pretty much the same as in VB6, though you'll not see it on the VS C# GUI. You'll have to get around the placement issues manually. Still - and most importantly -, you'll be able to refer to any instance by the index.
The following example is for 3 pieces for clarity, but of course you could automate every step of the process with appropriate loops.
public partial class Form1 : Form
{
...
Dictionary<int, UserControl1> NameOfUserControlInstance = new Dictionary<int, UserControl1>()
{
{ 1, new UserControl1 {}},
{ 2, new UserControl1 {}},
{ 3, new UserControl1 {}}
};
private void Form1_Load(object sender, EventArgs e)
{
NameOfUserControlInstance[1].Location = new System.Drawing.Point(0, 0);
NameOfUserControlInstance[2].Location = new System.Drawing.Point(200, 0);
NameOfUserControlInstance[3].Location = new System.Drawing.Point(400, 0);
Controls.Add(NameOfUserControlInstance[1]);
Controls.Add(NameOfUserControlInstance[2]);
Controls.Add(NameOfUserControlInstance[3]);
}
...
}
I like using Tags to apply any type of meta data about the controls
for (int i = 0; i< 10; ++i)
{
Button button = new Button();
button.Tag = i;
}

WPF ContentControl: Iterating over child control

I have a WPF Window, where I put a ContentControl, Later in my code I use a string to be read as Xaml using XamlReader.Load function and put that in ContentControl. This is done to make a Dyanmic UI. Now all is done, but I want to capture the Input field values from this control. on button click.
So, all I want to do is to Iterate on Child Controls of ContentControl. How can I do this, there doesn't seems a way to iterate on child of it? Any Idea. Thanks.
Here, you can use VisualTreeHelper:
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(contentControl); i++)
{
var child = VisualTreeHelper.GetChild(contentControl, i);
if(child is ContentPresenter)
{
var contentPresenter = child as ContentPresenter;
for (int j = 0; j < VisualTreeHelper.GetChildrenCount(contentPresenter); j++)
{
var innerChild = VisualTreeHelper.GetChild(contentPresenter, j);
}
break;
}
}
The difference between the logical and visual tree is, that the visual tree lists all elements which are used to display the control. E.g. The visual tree of a button looks like
Button -> Border -> ContentPresenter -> TextBlock
The logical tree list just the controls itsself (as you declared in your xaml).
For further details, visit this site: http://wpftutorial.net/LogicalAndVisualTree.html
So to get the children you want, LogicalTreeHelper.GetChildren(contentControl); should work.

Different-sized Tiles in Windows App

I know that I can use the ListView and GridView to create "Tiles"/Items whatever size I want, but how can I create different-sized Tiles for use within my app? This will need to work with a ListView or GridView.
I have tried so many things but I just have absolutely no idea how to do this. Any help will be much appreciated.
In case I haven't described what I am trying to achieve properly, here is a pic:
A simple way is to create a new class inheriting from GridView and override the PrepareContainerForItemOverride method. In which you can set the Column Span and RowSpan to Child item based on the model data. Consider your model class contains the Spanning information.
public class VariableGrid : GridView
{
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
ITileItem tile = item as ITileItem;
if (tile != null)
{
GridViewItem griditem = element as GridViewItem;
if (griditem != null)
{
VariableSizedWrapGrid.SetColumnSpan(griditem, tile.ColumnSpan);
VariableSizedWrapGrid.SetRowSpan(griditem, tile.RowSpan);
}
}
base.PrepareContainerForItemOverride(element, item);
}
}
More information : http://wpfplayground.blogspot.in/2013/03/different-sized-tile-items-in-winrt.html
You need to set ItemsPanel/ItemsPanelTemplate of your list to VariableSizedWrapGrid and set Grid.RowSpan/ColumnSpan of your list items to the values you want. I believe you can do that in ItemContainerStyle of the list control, which is best extracted by right-clicking the control in VS XAML design view or in Blend and selecting "Edit Additional Templates"/"Edit Generated Item Container".

Categories