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);
Related
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;
}
With WinForms programs I've become accustomed to marking the Modifiers property of a control as 'Private' to prevent external classes and whatever else have you from being able to see and mess with them.
Being still very green with WPF, I see no obvious equivalent in WPF that allows me to make it so external classes cannot see a control I drop onto a form or another user control or what not. I did notice something of x:FieldModifier = "Private" but I get the error "x:FieldModifier = "Private" is not valid for the language C#".
How do I mark a control as Private so it cannot be viewed or accessed by external class objects?
TL;DR
Most of the time you don't need to worry about this in WPF. However:
If you name a XAML element using the x:Name attribute, then you can use the x:FieldModifier attribute to control the visibility of the auto-generated field representing that element. This attribute value is language- and case-specific.
If you don't name a XAML element, then don't bother using the x:FieldModifier attribute.
Read on for a more detailed explanation.
Explicit naming and generated fields
If you create a new WPF application project in Visual Studio, it will create a MainWindow class, the XAML for which looks something like this:
<Window x:Class="StackOverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
</Grid>
</Window>
If you look at the code-behind class for this window, it will look like this:
// Several using statements...
namespace StackOverflow
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
Note the use of the partial keyword to denote this as a partial class. If you navigate to the project's obj\Debug folder using Windows Explorer, you will find a file called MainWindow.g.cs: it is this file that contains the code generated by the IDE from your XAML (it is basically the equivalent of the *.Designer.cs file from WinForms).
Your window has a Grid on it, but note that it is not surfaced directly anywhere in the code for MainWindow. Now edit your XAML to give the Grid a name:
<Grid x:Name="_myGrid">
Compile the application, and open the MainWindow.g.cs file again. You will see that the following line has been added:
internal System.Windows.Controls.Grid _myGrid;
Setting the x:Name property of the element in the XAML has caused the code generator to add a field with that name. The field is marked as internal which means it is accessible to all types in your project, but not to any other projects that reference your project.
So basically, if you do not explicitly name an element in the XAML using the x:Name attribute, the code generator will not create a named field for the element in the code-behind class, and your element will effectively be private (this means that the class itself cannot access the element directly either).
Nameless UI elements can still be accessed from code (if you have an instance)
An element without a name can still be accessed via code, by "walking" the visual tree of a Window instance. For example, because the window's content is set to a single Grid element, you can access that grid through code like so:
Grid grid = (Grid) this.Content;
this here refers to the MainWindow class instance.
WinForms has exactly the same "problem" as WPF in this regard: even controls that are not explicitly named can still be accessed through code. Imagine a WinForms Form with a single Button control on it. You can access that button like so:
Button button = (Button) this.Controls[0];
The fact that the button had a default Modifiers value of "Private" did not stop the code from being able to access it.
The FieldModifier attribute controls generated field visibility
Coming back to WPF, particularly if you're using the Model-View-ViewModel (MVVM) pattern, you will rarely need to explicitly name your elements in the XAML, hence the default behaviour will be fine. However, if you do find that you need to name your XAML elements, and you wish to "hide" these elements, then you can use the x:FieldModifier attribute to set the visibility of an element to private instead of the default internal. The value used for the attribute is language-dependent and case-sensitive, eg. for C#:
<Grid x:Name="_myGrid" x:FieldModifier="private">
I have a sub project wich is a class and contains DataLib.cs and an user controll MediumTile.xaml. This user control will be the generated to an image to use it as tile background. But before I have to change a few thing dynamicly. So how can I get controll above the LayoutRoot inside MediumTile.xaml for example to set the background color?
Something like this:
MediumTile.LayoutRoot.Background = new SolidColorBrush(Color.FromArgb(255, 206, 23, 23);
MediumTile.xaml probably exists in some kind of namespace.
You can find the namespace of the UserControl in the top of the file beside the x:Class declaration.
Typically it'll look something like
x:Class="MyProject.UserControls.MediumTile"
if your project is set up normally.
If you look at the MediumTile.xaml.cs, you should see a namespace like so
namespace MyProject.UserControls
{
public partial class MediumTile : UserControl
...
First off, you will need to reference your subproject.
Assuming you have a project structure like so...
CurrentProject/
-MyPage.xaml
SubProject/
-MediumTile.xaml
Right-click your Solution in Visual Studio and click Properties.
Under Properties select Project Dependencies.
Choose the CurrentProject in the dropdown.
In the Depends On checkbox field, choose the SubProject.
Click on StartUp Project in the side bar.
Make sure Single StartUp Project points to CurrentProject. If not, set it.
Now you're done setting up, you'll need to actually use MediumTile.xaml now.
To use the MediumTile UserControl in another XAML file, you will need to declare
xmlns:customControls="clr-namespace:MyProject.UserControls"
inside the page header, and call
<ListBox.ItemTemplate>
<DataTemplate>
<customControls:MediumTile/>
...
To use this UserControl in another CS file, you will need to import the namespace
using MyProject.UserControls;
at the top of the page, and reference your control like so (depending on your usercontrol's constructor),
MediumTile mediumTile = new MediumTile()
About your LayoutRoot problem, you can simply set the Background color directly on the UserControl. UserControl inherits from Control, which has the Background property already.
I've never done it for windows phone 8, but for normal desktop applications, you can do it by adding the following references:
PresentationCore
PresentationFramework
WindowsBase
Then you can create and access a Control in a normal way.
I am working on windows application form. I have a CustomControl (say MasterControl) on which i put a split panel and now my MasterControl is split into three parts say:
Pannel1
Pannel2
Pannel3
Now i develop three custom controls and put one in each of pannels e.g
Pannel1 have CustomControl1
Pannel2 have CustomControl2
Pannel3 have CustomControl3
Now somewhere in CustomControl3 I need to access a public member of CustomControl1. For which i wrote the following code:
((MasterControl)this.Parent)._oCustomControl1.PublicMember = this.PublicMember;
The code above doen't work in my case. When this line of code is executed in debug mode then a message box appears and states that "There is no code available for current location"
It's a really bad design for your controls to depend on how are the arranged on the parent container.
e.g. inside your third control, you are quering the property of the first one by accessing it from the parent, and then it's child control by name.
Your code will break very easily, if it can be compiled at all - I think the problem you're having is the order of compilation: in order for your parent form to be compiled, it needs to have child user controls finished. On the other hand the user controls you created need to have finished form.
It would be far better to set whatever behaviour you're after from the container of those controls - for example, by reacting to events from the control, and setting appropriate stuff on appropriate other controls (there are other ways as well ofcourse - the point is in the direction and flow of information - who's setting and using what).
If you have a split panel in your master control, you should go two levels up to find your master control:
((MasterControl)this.Parent.Parent)._oCustomControl1.PublicMember = this.PublicMember;
I found the answer by myself. I am positing here because it might help some one else.
The exact code is:
((MasterControl)this.Parent.Parent.Parent)._oCustomControl1.PublicMember = this.PublicMember;
Basically my coustomcontrol3 lies inside a split container panel, so when i wrote:
this.Parent then it points to Panel In which it is residing and if i wrote
this.Parent.Parent then it points to the spliter container in which above panel reside and if i wrote
this.Parent.Parent.Parent then it points to control in which this split container resides
I got the idea from "Farzin Zaker" answer, so thanks to him for his contribution
I have a lot of different UserControls and would like to maintain consistent UI settings (mainly colors and fonts). My first try was this:
public class UISettings
{
//...
public void SetupUserControl(ref UserControl ctrl)
{
ctrl.BackColor = this.BackColor;
}
}
to be called in every control like this:
settings.SetupUserControl(ref this);
As this is read-only it cannot be passed by ref argument so this does not work. What are other options to keep consistent UI without manually changing properties for every item?
Inheritance! If you have a form or control that will constantly be using the same styles and you want to set that as your base, just create your own user controls that inherit from a form/control. By default all of your forms will inherit from "Form". Instead of inheriting from the default form, create a new user control that inherits from Form, and then have that as your base class.
CustomForm : Form // Your custom form.
Form1 : CustomForm // Inherit from it.
...the same works for components. If you want a button to have the same styles across the board, create a user control and have it inherit from the button control -- then use the custom control.
Whenever you want to make a change to your base styles, or any settings, simply change your custom controls settings -- your new forms/controls will automatically be updated!
Do the same thing. Don't pass it by ref. UserControl is a reference object already, so there's no need to pass it into your method using the ref keyword.
You may also want to consider a recursive method that will find all the UserControls on the form and pass it into your method.
How about a base class which provides such settings?
Two answers:
You don't need ref, controls are objects are reference types. Just drop it.
Create a Base UserControl and derive your controls form that base. You can still do that, just edit the class definitions of the controls. For new controls you can follow the Wizard.
A tip: setup the styling in the baseControl. Then make sure the derived controls don't override, the best way to do that is scanning the *.Designer.cs files and remove all settings that you know should come from the base.