Is there a way to build a DataTemplate without using the deprecated FrameworkElementFactory or the XamlReader.Load method of string interpretation to code (related question) or (another related)?
DataTemplate dt = new DataTemplate();
StackPanel sp = new StackPanel();
ComboBox cellComboBox = new ComboBox() { Visibility = Visibility.Collapsed };
CheckBox cellCheckBox = new CheckBox() { Visibility = Visibility.Collapsed };
sp.Children.Add(cellComboBox);
sp.Children.Add(cellCheckBox);
// then add the stackpanel somehow?
Update: Why?
This is for a new program that may have to be supported for years. XamlReader string parsing at runtime for something that's got a constructor, methods, properties... feels kludgy. The FrameworkElementFactory has been deprecated for several versions but has never had a real replacement.
Is there a way to build a DataTemplate with only C#
Yes, you use either the FrameworkElementFactory or the XamlReader.Load method to create a DataTemplate programmatically.
...without using the deprecated FrameworkElementFactory or the XamlReader.Load method?
Short answer: No.
Why would you need yet another way when there are two already? Also note that it's perfectly fine to use the FrameworkElementFactory class if you don't want to use strings. Despite what the documentation on MSDN says, this type is not really marked as obsolete or deprecated in .NET Framework 4.7.2.
To elaborate on #mm8's answer: no, because if you inspect the FrameworkTemplate.LoadContent method you'll find that a template is basically a wrapper that uses either a FrameworkElementFactory or a TemplateContent to load the contents, TemplateContent being a utility to read XAML node stream. So these are the only two ways in which a template can operate.
Personally, of the two, I'd recommend using XAML (as a separate *.xaml file rather than a string), because it's far more readable, but also because FrameworkElementFactory has limited functionality, i.e. it only supports dependency properties and routed events (you cannot create a factory that would set a regular CLR property or event handler). It has one advantage over XAML though - it does support generic classes, so perhaps a mix of the two is the most proper approach.
Related
I am trying to get multilingual translation("label or caption") string value for the given elementname in wpf.
For example; for elementname "txtDescription" ; my IValueConverter implementation will return with "Description" ; for another language will return different translation string(i.e. descripciĆ³n for Spanish) and the translation string will be Text=... of the same element.
I am new to wpf; I cant make it work. Is there any elegant way to do that with similiar manner as below.
<TextBlock Name="txtDescription" Text="{Binding Converter={StaticResource MultiLingualConverter} }"</TextBlock>
If this is not simple or requires more code then as an alternative sending "txtDescription" as an argument to MultiLingualConverter is acceptable but I dont now how to do that neither.
Definitely, you should build good localization support in your system, better then using converter for every string.
Until today, the best solution i have found and i almost always use it is this:
http://blogs.microsoft.co.il/tomershamam/2007/10/30/wpf-localization-on-the-fly-language-selection/
give it a try. good luck
You can pass the element name via the ConverterParameter property, this is an example of how it is used to pass a string to the converter.
However, WPF localization is more than returning different strings for different languages. You can read this article for more information and there is a Run Dialog Box example in the page to get you started.
Due to cyclical dependency restrictions you cannot reference a control inside itself or its descendants in the tree.
I would recommend you not to use converter for multilingual support. it is not a best way to do this.
got here for best way to do.
https://msdn.microsoft.com/en-us/library/ms745650(v=vs.110).aspx
I am not sure if this is a bug in 4.0 or a misunderstanding of DP's and bindings on my part.
*Note: this works in 3.5 and 4.5...just not 4.0
Consider the following code:
VM:
public MyEnum MyProp
{
get{return _myProp;}
set
{
_myProp = value;
OnPropertyChanged("MyProp");
}
}
View(Dependency Property setup):
MyDP= DependencyProperty.Register("MyProp", typeof(MyEnum), typeof(MyControl), new PropertyMetadata(DefaultEnumVal, MyPropChanged));
View(in the datacontextchanged event):
First Option
this.SetBinding(MyDP, "MyProp"); //Only triggers MyPropChanged once
Second Option
this.SetBinding(MyDP,
new Binding
{
Source=this.DataContext,
Path = new PropertyPath("MyProp")
}
); //Works as expected
So, given all of that, why does the first option only trigger the property changed once, and the more verbose method continues beyond the first change? I thought that the first/string version inherently used the this.DataContext? If I install .NET 4.5, then this works as expected for both, which leads me to believe that this is a 4.0 bug, possibly related to doing this in the DataContextChanged event?
But, maybe I am missing something, so that is why I am asking here :)
UPDATE
Even per Microsoft:
By default, bindings inherit the data context specified by the
DataContext property, if one has been set
And, per reflection:
public BindingExpression SetBinding(DependencyProperty dp, string path)
{
return (BindingExpression)this.SetBinding(dp, new Binding(path));
}
So, all this does is call the same code as above, just without setting the source. This should result in the update working against the DataContext
I am able to reproduce the first option if I leave the Source null in the second option
UPDATE #2
Apparently in 4.5, they added a ResolvedSource property. This shows that in 4.5, the source resolves to what I expect. I traced that to the ClrWorker and found that the SourceItem is indeed being evaluated correctly...at least at the time of SetBinding. I am going to see if there is a way for me to keep track of this to see if/when it changes.
UPDATE #3
After further debugging, I found that the changed event only occurs on the SetBinding. If I set the DefaultEnumValue to be what it is when the SetBinding occurs, then the change does not trigger at all
UPDATE #4
If I call this.SetValue(MyDP, SomeEnumVal);* then this works and even writes into the VM. I have tried changing the DP so that the metadata is set to be twowaybindingbydefault to see if that helps, but it did not
*This does not surprise me since SetBinding itself does a target.SetValue(dp, bindingExpressionBase);, which explains why it works one time.
It certainly seems odd, but I think what's happening here is not related to the Binding's Source, but how you're setting the Path. Your first case calls SetBinding(DependencyProperty, string), which in turn calls SetBinding(DependencyProperty, new Binding(path)). The Binding constructor internally sets the Path to be new PropertyPath(path, new object[0]) - a path with no parameters.
In the second case, you're creating your own PropertyPath, but via a different constructor that takes a single argument of type object, which represents a parameter. That constructor seems to set Path to be "(0)" and sets your "MyProp" string as a parameter to the binding.
Here's what MSDN says about that constructor:
This constructor has two completely different usages depending on
whether it is being used for a source-mode property path for a
binding, or for a target-mode single-step property path for a
storyboard target.
If using this PropertyPath in source mode for a binding, parameter is
a string representing a property name, or can be a string that
describes a "step-through" path to the property in the CLR object
model of the object that is being used as the source for a binding.
For a binding property path, the character that identifies a "step" is
a dot (.). Indexer references (including multiple indexers, and type
differentiation) are also supported. For more details on the syntax of
the string as specifically used by the Binding object, see
Binding.Path. A property used as a binding source need not be a
dependency property. If the binding updates two-way, the property
referenced must be read-write. Also note that the binding target does
have to be a dependency property. For details, see Data Binding
Overview.
If using this PropertyPath in target mode for a single-step path for a
storyboard target, parameter is generally provided as type
DependencyProperty. You can also specify a string that is the Name.
Either of these evaluate to the same result, because it is stored
internally as a string. A provided DependencyProperty is converted to
a string through DependencyPropertyConverter. The
DependencyPropertyConverter supports a qualified naming format for
dependency properties, so you can specify a typeName.propertyName
qualified dependency property name string to the
PropertyPath.PropertyPath constructor in code. The qualified path to
the dependency property identifier is a different concept than a
complex path. A complex-path PropertyPath should instead be created
with the PropertyPath.PropertyPath constructor.
I'm still not sure why your first case doesn't work, but imagine that for some reason, since the parameter doesn't have to be a dependency property but can be a regular CLR property, its being treated as the latter.
In the second case, the property is being interpreted as a "single-step path for a storyboard target" (even though I imagine its probably not), but since in this usage it must be a dependency property, the binding handles it differently.
For consistency, you could try in your second case to set Path as:
Path = new PropertyPath("MyProp", new object[0])
Of course, now I would expect your binding to only work once in both cases! It might restore some sanity, but could also indicate that the binding problem lies elsewhere.
The class allows the propagations of data context from the collection to its members. But it is only for SilverLight.
More info for DepedencyObjectCollection can be found from this post.
If the equivalent doesn't exist, what's the necessary steps required to create one?
I observe the desire behavior from the RowDefinitionCollection class. But I couldn't figure out its implementation.
thanks
The short answer is NO.
But I no longer think it is the correct implementation anyway. I end up using a collection of FrameworkElement and add them into the LogicalChildren of the custom control. By doing so, DataContext are passed on to the FrameworkElement naturally and everything works as expected.
It seems that that's how RowDefinition class is implemented in .NET 4.0. However, Silverlight version of RowDefinition is derived from DependencyObject directly. The MSDN document shows Silverlight documentation by default and misdirected me to the older and incorrect approach.
I'm fairly new to WP7 and totally new to Expression Blend.
I have a ListBox bound to a List of custom objects,
List<Person>
Each item in the list contains a custom control, MyControl which is bound to Person.
MyControl contains a TextBox which is bound to the Username property of Person.
All of this works fine. My question is: how do I set a default value for the TextBlock so that it becomes visible in the Designer or ExpressionBlend? With it being data bound, it has no text till it runs ... so I can't actually do any fancy styling using these wonderful tools unless I repeatedly delete the binding code to replace it with a string, make the changes, replace the binding code, repeat. Seems long winded!
Thanks,
Steven
What you want is "Design time data".
There are a number of ways of doing this. Fortunately there are also lots of resources online which explain it.
#Steven Have you looked at creating sample data in Blend to do what you require and then some binding to actually attached the data to the control bound to your list? You might like to check out Blend Sample Data as it guides you through a simple example of doing just that. You might then be able to adapt to to your own ends.
It depends if you are using any MVVM model or not.
My suggestion, if you are not using a MVVM, is to use Blend Sample data, is fast and quick.
If you are MVVM Light I've found very usefull to create two files:
DataService.cs - contains the real connection and data
DesignDataService.cs - contains the sample data
The two libraries are identical, from an call perspective so that in the ViewModelLocator you can swap them:
if (ViewModelBase.IsInDesignModeStatic)
{
SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
}
else
{
//SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
SimpleIoc.Default.Register<IDataService, DataService>();
}
In the Design class I've decided to create an XML file for each Model so that it's easy to change the sample data and test all possible scenarios.
I then use the Deserialize function to read it:
csNodeList _Copyrights = new csNodeList();
resource = System.Windows.Application.GetResourceStream(new Uri(#"Design/sampledata.xml", UriKind.Relative));
streamReader = new StreamReader(resource.Stream);
serializer = new XmlSerializer(typeof(csNodeList));
_Copyrights = (csNodeList)serializer.Deserialize(streamReader);
Please note that the file sampledata.xml has to be stored in folder Design and must be defined as Content not as Resource.
It is suggested to improve performance and load time.
M
I'm trying to create a DataGridTableStyle object so that I can control the column widths of a DataGrid. I've created a BindingSource object bound to a List. Actually it's bound to an anonymous type list created though Linq in the following manner (variable names changed for clarity of what I'm doing):
List<myType> myList = new List<myType>(someCapacity);
.
...populate the list with query from database...
.
var query = from i in myList
select new
{
i.FieldA,
i.FieldB,
i.FieldC
};
myBindingSource.DataSource = query;
myDataGrid.DataSource = myBindingSource;
Then I create a DataGridTableStyle object and add it to the datagrid. However, it never applies my table style properties I set up because I can't seem set the proper myDataGridTableStyle.MappingName property.
I've searched Google for about 1/2 an hour and keep seeing links to the same question throughout a bunch of different forums (literally the same text, like someone just copied and pasted the question... I hate that...). Anyway, none of the suggestions work, just like the guy says on all the other sites.
So does anybody here know what I need to set the MappingName property to in order to have my TableStyle actually work properly? Where can I grab the name from? (It can't be blank... that only works with a BindingSource that is bound to a DataTable or SqlCeResultSet etc.).
I'm thinking it could be an issue with me using Linq to create an anonymous, more specialized version of the objects with only the fields I need. Should I just try to bind the BindingSource directly to the List object? Or maybe even bind the DataGrid directly to the List object and skip the binding source altogether.
Thanks
PS - C#, Compact Framework v3.5
UPDATE:
I've posted an answer below that solved my problem. Whether or not it's the best approach, it did work. Worth a peek if you're having the same issue I had.
I've found the way to make this work. I'll break it out into sections...
List<myType> myList = new List<myType>(someCapacity);
.
...populate the list with query from database...
.
DataGridTableStyle myDataGridTableStyle = new DatGridtTableStyle();
DataGridTextBoxColumn colA = new DataGridTextBoxColumn();
DataGridTextBoxColumn colB = new DataGridTextBoxColumn();
DataGridTextBoxColumn colC = new DataGridTextBoxColumn();
colA.MappingName = "FieldA";
colA.HeaderText = "Field A";
colA.Width = 50; // or whatever;
colB.MappingName = "FieldB";
.
... etc. (lather, rinse, repeat for each column I want)
.
myDataGridTableStyle.GridColumnStyles.Add(colA);
myDataGridTableStyle.GridColumnStyles.Add(colB);
myDataGridTableStyle.GridColumnStyles.Add(colC);
var query = from i in myList
select new
{
i.FieldA,
i.FieldB,
i.FieldC
};
myBindingSource.DataSource = query.ToList(); // Thanks Marc Gravell
// wasn't sure what else to pass in here, but null worked.
myDataGridTableStyle.MappingName = myBindingSource.GetListName(null);
myDataGrid.TableStyles.Clear(); // Recommended on MSDN in the code examples.
myDataGrid.TablesStyles.Add(myDataGridTableStyle);
myDataGrid.DataSource = myBindingSource;
So basically, the DataGridTableStyle.MappingName needs to know what type of object it is mapping to. Since my object is an anonymous type (created with Linq), I don't know what it is until runtime. After I bind the list of the anonymous type to the binding source, I can use BindingSource.GetListName(null) to get the string representation of the anonymous type.
One thing to note. If I just bound the myList (which is type "myType") directly to the binding source, I could have just used the string "myType" as the value for DataGridTableStyle.MappingName.
Hopefully this is useful to other people!
Just to add to the collection of answers already on this page....
I was just frustrated with this same issue trying to develop my fist application using windows forms and compact framework (For Windows Mobile 6.5).
What I found out, through Marc Gravell's comment above is that indeed is possible to get the run time MappingName inspecting the properties of the DataGrid. Doing this I found out that when binding my List<MyType> directly to the DataSource property of the DataGrid, the DataGrid was actually looking for a DataGridTableStyle with the MappingName of
"List`1"
instead of any combination of List<MyType> or MyType...
So... by putting "List`1" in the Mapping name on the DataGridTableStyle Collection Editor (at design time), I was able to customize the columns and other properties without having to create them all at run time.
I just hope this adds some more to the answers already provided. Thank you all for providing me with the guidelines.
The query returns IEnumerable<T> for some T, but most binding sources (except ASP.NET) require IList (such as any IList<T> implementation) - try adding .ToList() - i.e.
myBindingSource.DataSource = query.ToList();
A BindingList<T> might work even better (if it is supported in CF 3.5) since it has better support for some of the common binding scenarios; if you need this (and assuming BindingList<T> exists on CF 3.5), you can add an extension method:
static BindingList<T> ToBindingList<T>(this IEnumerable<T> data)
{
return new BindingList<T>(new List<T>(data));
}
then call:
myBindingSource.DataSource = query.ToBindingList();
For completeness, an alternative to an IList is IListSource (or even Type for purely-metadata scenarios), which is why DataSource is commonly typed as object; if it wasn't for this issue, the compiler probably would have been able to tell you the problem (i.e. if DataSource was defined as IList).
I followed this answer and found that the MappingName always came out to be the underlying class name (myType in the example).
So it seems that putting the collection it through the BindingSource solves the problem anyway and that there is then no need for BindingSource.GetListName(null).
Also I found no need to ToList() the query as the BindingSource will also do this for you.
Many thanks to Jason Down for putting me on the right track.
I was facing same problem for setting column width.
After lot of R & D, i changed code as below and its working fine.
Code:
DataGridTableStyle tableStyle = new DataGridTableStyle();
tableStyle.MappingName = dgCustom.DataSource.GetType().Name;
where dgCustom is DataGrid ID in dgCustom.DataSource.GetType().Name which is working perfectly.