In WPF, writing
<TextBlock x:Name="foo"/>
will make the control public. To make it private, one must explicitly specify the FieldModifier:
<TextBlock x:Name="foo" x:FieldModifier="private"/>
I find this strange. I don't think it is a good coding style to access a subcontrol directly from outside the class. For example, I would avoid writing
var muc = new MyUserControl();
muc.foo.Text = "foo";
Instead I would write a public method and use it:
public void SetFooText(string text) { foo.Text = text; }
// in somewhere else
var muc = new MyUserControl();
muc.SetFooText("foo");
or write a public property
public string FooText
{
get { return foo.Text; }
set { foo.Text = value; }
}
// in somewhere else
var muc = new MyUserControl();
muc.FooText = "foo";
So, I don't really see any advantages setting controls to public by default. Maybe it would be safer if private is the default, like everything in C#.
Why is public the default?
Edit:
Well, I made a mistake. The default is internal as others have mentioned. But the question why it is not private is still waiting for an answer.
Default value of the x:FieldModifier for C# is NotPublic(Internal)
TypeAttributes.NotPublic is the default behavior because it is infrequent that code outside the assembly that compiled the XAML needs access to a XAML-created element. WPF security architecture together with XAML compilation behavior will not declare fields that store element instances as public, unless you specifically set the x:FieldModifier to allow public access.
As we see if they were private by default assembly which compiled the XAML would not have access to it XAML-created element.
You can find more information here MSDN
Related
Can somebody provide an overview for use of the XamlBindingHelper class with examples? Specifically the GetDataTemplateComponent and SetDataTemplateComponent method.
In the official document, it says
This class is for use in code that is generated by the XAML compiler.
This tells me that I should be able to find some reference of it in code-generated classes (.g.cs) by x:Bind, given there's not a single thread on the Internet that explains what exactly it does.
So I created a test UWP project with a ListView, and inside its ItemTemplate I threw in some x:Bind with x:Phase. After I compiled the project, I found some of its methods used inside my MainPage.g.cs -
XamlBindingHelper.ConvertValue
public static void Set_Windows_UI_Xaml_Controls_ItemsControl_ItemsSource(global::Windows.UI.Xaml.Controls.ItemsControl obj, global::System.Object value, string targetNullValue)
{
if (value == null && targetNullValue != null)
{
value = (global::System.Object) global::Windows.UI.Xaml.Markup.XamlBindingHelper.ConvertValue(typeof(global::System.Object), targetNullValue);
}
obj.ItemsSource = value;
}
Apparently the XamlBindingHelper.ConvertValue method is for converting values. I knew this already, as I used it in one of my recent answers on SO.
XamlBindingHelper.SuspendRendering & XamlBindingHelper.ResumeRendering
public int ProcessBindings(global::Windows.UI.Xaml.Controls.ContainerContentChangingEventArgs args)
{
int nextPhase = -1;
switch(args.Phase)
{
case 0:
nextPhase = 1;
this.SetDataRoot(args.Item);
if (!removedDataContextHandler)
{
removedDataContextHandler = true;
((global::Windows.UI.Xaml.Controls.StackPanel)args.ItemContainer.ContentTemplateRoot).DataContextChanged -= this.DataContextChangedHandler;
}
this.initialized = true;
break;
case 1:
global::Windows.UI.Xaml.Markup.XamlBindingHelper.ResumeRendering(this.obj4);
nextPhase = -1;
break;
}
this.Update_((global::System.String) args.Item, 1 << (int)args.Phase);
return nextPhase;
}
public void ResetTemplate()
{
this.bindingsTracking.ReleaseAllListeners();
global::Windows.UI.Xaml.Markup.XamlBindingHelper.SuspendRendering(this.obj4);
}
XamlBindingHelper.SuspendRendering & XamlBindingHelper.ResumeRendering look very interesting. They seem to be the key functions to enable ListView/GridView's incremental item rendering which helps improve the overall panning/scrolling experience.
So apart from x:DeferLoadingStrategy and x:Load(Creators Update), they are something else that could be used to improve your app performance.
IDataTemplateComponent & IDataTemplateExtension
However, I couldn't find anything related to GetDataTemplateComponent and SetDataTemplateComponent. I even tried to manually set this attached property in XAML but the get method always returned null.
And here's the interesting bit. I later found this piece of code in the generated class.
case 2: // MainPage.xaml line 13
{
global::Windows.UI.Xaml.Controls.Grid element2 = (global::Windows.UI.Xaml.Controls.Grid)target;
MainPage_obj2_Bindings bindings = new MainPage_obj2_Bindings();
returnValue = bindings;
bindings.SetDataRoot(element2.DataContext);
element2.DataContextChanged += bindings.DataContextChangedHandler;
global::Windows.UI.Xaml.DataTemplate.SetExtensionInstance(element2, bindings);
}
break;
The method DataTemplate.SetExtensionInstance looks very similar to XamlBindingHelper.SetDataTemplateComponent. It takes element2 which is the root Grid inside the ItemTemplate of my ListView, and an IDataTemplateExtension; where the latter takes an element and an IDataTemplateComponent. If you have a look at their definitions, their functionalities are very similar, which makes me think if DataTemplate.SetExtensionInstance is the replacement of XamlBindingHelper.SetDataTemplateComponent? I'd love to know if otherwise.
Unlike IDataTemplateComponent, you can get an instance of the IDataTemplateExtension in your code -
var firstItemContainer = (ListViewItem)MyListView.ContainerFromIndex(0);
var rootGrid = (Grid)firstItemContainer?.ContentTemplateRoot;
var dataTemplateEx = DataTemplate.GetExtensionInstance(rootGrid);
In my case, the dataTemplateEx is an instance of another generated class called MainPage_obj2_Bindings, where you have access to methods like ResetTemplate and ProcessBindings.
I assume they could be helpful if you were to build your own custom list controls, but other than that I just can't see why you would ever need them.
This is a bit of a confusing scenario. Basically, I'm trying to set a button's color when I use setter for a private variable that I store its color in.
First off, I have a seperate window for customizing stuff. When I change the button color, I want to change every button in this window as well. I have it stored in a static variable in my main form class.
public static frm_custom customizer;
This is the setter for the variable in question.
private Color _buttonColor;
public Color buttonColor
{
get { return this._buttonColor; }
set
{
this.btn_input.BackColor = buttonColor;
this._buttonColor = buttonColor;
if (Application.OpenForms.OfType<frm_custom>().Count() == 1)
{
customizer.setButtonColor(buttonColor);
}
}
}
Strangely, it doesn't effect the color at all. Did I do something wrong?
Did I do something wrong?
Yes. Your setter is just fetching the existing property value:
this.btn_input.BackColor = buttonColor;
this._buttonColor = buttonColor;
You meant to use value, which is the implicit parameter name for the setter:
this.btn_input.BackColor = value;
this._buttonColor = value;
(Ditto for your if block, but it's hard to tell how that's meant to work as it's not valid C# at the moment.)
As a side note, I'd strongly urge you to start following .NET naming conventions, which include capital letters for properties - so ButtonColor rather than buttonColor.
This is an attempt to expand on this question. In my WPF program I've been cloning tabItems by using an XamlWriter in a function called TrycloneElement. I originally found this function here, but the function can also be viewed in the link to my previous question.
Now that I am beginning to worry about functionality inside my program, I found that the TrycloneElement function does not replicate any code-behind functionality assigned to the tabItem that it is cloning.
Because of High Core's link and comment on my earlier question I decided to start implementing functionality on my tabItems through Data Binding with my ViewModel.
Here is a sample of a command that I've implemented:
public viewModel()
{
allowReversing = new Command(allowReversing_Operations);
}
public Command AllowReversing
{
get { return allowReversing; }
}
private Command allowReversing;
private void allowReversing_Operations()
{
//Query for Window1
var mainWindow = Application.Current.Windows
.Cast<Window1>()
.FirstOrDefault(window => window is Window1) as Window1;
if (mainWindow.checkBox1.IsChecked == true) //Checked
{
mainWindow.checkBox9.IsEnabled = true;
mainWindow.groupBox7.IsEnabled = true;
}
else //UnChecked
{
mainWindow.checkBox9.IsEnabled = false;
mainWindow.checkBox9.IsChecked = false;
mainWindow.groupBox7.IsEnabled = false;
}
}
*NOTE: I know that I cheated and interacted directly with my View in the above code, but I wasn't sure how else to run those commands. If it is a problem, or there is another way, please show me how I can run those same commands without interacting with the View like I did.
Now to the question:
After changing my code and adding the commands to my ViewModel, the TrycloneElement function no longer works. At run time during the tab clone I receive an XamlParseException on line, object x = XamlReader.Load(xmlReader); that reads:
I'm fine with ditching the function if there is a better way and I don't need it anymore. But ultimately, how do I take a tabItem's design and functionality and clone it? (Please keep in mind that I really am trying to correct my structure)
Thank you for your help.
Revision of Leo's answer
This is the current version of Leo's answer that I have compiling. (There were some syntax errors)
public static IList<DependencyProperty> GetAllProperties(DependencyObject obj)
{
return (from PropertyDescriptor pd in TypeDescriptor.GetProperties(obj, new Attribute[] { new PropertyFilterAttribute(PropertyFilterOptions.SetValues) })
select DependencyPropertyDescriptor.FromProperty(pd)
into dpd
where dpd != null
select dpd.DependencyProperty).ToList();
}
public static void CopyPropertiesFrom(this FrameworkElement controlToSet,
FrameworkElement controlToCopy)
{
foreach (var dependencyValue in GetAllProperties(controlToCopy)
.Where((item) => !item.ReadOnly)
.ToDictionary(dependencyProperty => dependencyProperty, controlToCopy.GetValue))
{
controlToSet.SetValue(dependencyValue.Key, dependencyValue.Value);
}
}
Here is my example of a properly-implemented dynamic TabControl in WPF.
The main idea is that each Tab Item is a separate widget that contains its own logic and data, which is handled by the ViewModel, while the UI does what the UI must do: show data, not contain data.
The bottom line is that all data and functionality is managed at the ViewModel / Model levels, and since the TabControl is bound to an ObservableCollection, you simply add another element to that Collection whenever you need to add a new Tab.
This removes the need for "cloning" the UI or do any other weird manipulations with it.
1.) To fix that XamlParseException, make sure you have a public constructor like an empty one, you probably defined a constructor and when you tried to serialize that object and deserialize it can't. You have to explicitly add the default constructor.
2.) I don't like the word clone, but I'd say, when they want to copy. I'll manually create a new tab item control then do reflection on it.
I have this code that I made
public static IList<DependencyProperty> GetAllProperties(DependencyObject obj)
{
return (from PropertyDescriptor pd in TypeDescriptor.GetProperties(obj, new Attribute[] {new PropertyFilterAttribute(PropertyFilterOptions.SetValues)})
select DependencyPropertyDescriptor.FromProperty(pd)
into dpd where dpd != null select dpd.DependencyProperty).ToList();
}
public static void CopyPropertiesFrom(this FrameworkElement controlToSet,
FrameworkElement controlToCopy)
{
foreach (var dependencyValue in GetAllProperties(controlToCopy)
.Where((item) => !item.ReadOnly))
.ToDictionary(dependencyProperty => dependencyProperty, controlToCopy.GetValue))
{
controlToSet.SetValue(dependencyValue.Key, dependencyValue.Value);
}
}
So it would be like
var newTabItem = new TabItem();
newTabItem.CopyPropertiesFrom(masterTab);
eternal question about twoForms:
frm02 frm02 = new frm02();
frm02.Text = "Objects";
ds02 = new DataSet();
ds02.ReadXml(path02);
frm02.dgv02.DataSource = ds02.Tables[0]; //error: dgv02 is inaccessible...
frm02.ShowDialog();
pleaseHelp!
You need to make dgv02 public. By default, when you add types via the designer, they are not public. You can edit their properties in the design window, and change the accessibility level to public.
That being said, a better option might be to create a public property that returns the appropriate control, or, even better, allows you to set the data. For example, if you add this method to your form:
public void SetDatasource(DataSet data)
{
this.dgv02.DataSource = data;
}
You could then call this as:
frm02.SetDatasource(ds02);
I added a new property to a component to uniquely identify every gridcontrol in my project, called GridIdentifier:
public class MyCustomGridControl : GridControl
{
private string gridIdentifier = "empty";
[Browsable(true)]
[DefaultValue("empty")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string GridIdentifier
{
get { return gridIdentifier; }
set { gridIdentifier = value; }
}
public MyCustomGridControl()
{
if (this.gridIdentifier == "empty")
this.gridIdentifier = Guid.NewGuid().ToString();
}
}
The problem is that for existing controls in my forms, the form only serializes the new property after I change something (read: anything) within the form. It might be the caption of the form, the size, etc.
But what I would like to see is that it detects that the form has changed when I open it, so I can save it and the new property gets serialized.
Does anyone have a clue why the new property doesn't get saved after opening the form and how to fix it? Any other ideas that help are of course also appreciated.
I would guess it is doing basic sanity checking (i.e. should anything have changed) - to prevent unexpected source code changes (I hate it when opening a file can cause side-effects - I'm looking at you, DBML!).
On a side note, to force serialization generally (I don't think it will apply due to the above):
public bool ShouldSerializeGridIdentifier() { return true; }
the bool ShouldSerialize*() and void Reset*() are a convention used by the framework.