This is XAML code;
<toolkit:AutoCompleteBox x:Name="newTaskNameTextBox"
ItemsSource="{StaticResource BankNamesList}" />
How to assign this ItemSource attribute to the newTaskNameTextBox by C# programmatically ?
(Solution for WPF)
You should use the TryFindResource method.
newTaskNameTextBox.ItemsSource =
(IEnumerable)newTaskNameTextBox.TryFindResource("BankNamesList");
This searches up the logical tree, in the same way {StaticResource BankNamesList} does.
UPDATE: (solution for WP8)
Sounds lile you're using WP8 (which doesn't include FindResource / TryFindResource) so try this instead:
newTaskNameTextBox.ItemsSource = (IEnumerable)Resources["BankNamesList"];
UPDATE: (how to implement the missing TryFindResource)
Note that the code above requires the resource to exist in the owner of this code behind (e.g. the window). However, there may be cases where the resource exists in another parent element up the logical tree. For example, you may be writing the code behind for a custom user control but the resource you're looking for exists in the MainWindow. For such cases, it wouldn't be too hard to write a basic implementation of WPF's TryFindResouces, which has the advantage of searching up the logical tree (source link):
public static class FrameworkElementExtensions
{
public static object TryFindResource(this FrameworkElement element, object resourceKey)
{
var currentElement = element;
while (currentElement != null)
{
var resource = currentElement.Resources[resourceKey];
if (resource != null)
{
return resource;
}
currentElement = currentElement.Parent as FrameworkElement;
}
return Application.Current.Resources[resourceKey];
}
}
/**********************************************************************/
// Or, the recursive version of TryFindResource method as suggested by #Default:
public static object TryFindResource(this FrameworkElement element, object resourceKey)
{
if (element == null)
return Application.Current.Resources[resourceKey];
var resource = element.Resources[resourceKey];
if (resource != null)
{
return resource;
}
return TryFindResource(element.Parent, resourceKey);
}
So if you include this FrameworkElementExtensions class in your namespace, then you should be able to do this (same code I've given for WPF originally):
newTaskNameTextBox.ItemsSource =
(IEnumerable)newTaskNameTextBox.TryFindResource("BankNamesList");
If BankNamesList is resource in the resources of your window, then in code behind you can do:
newTaskNameTextBox.ItemsSource = Resources["BankNamesList"]
Try this:
newTaskNameTextBox.ItemsSource = (IEnumerable)(Application.Current.Resources["BankNamesList"]);
Related
I am working on a windows phone app. I want to copy children of one canvas to other canvas. I can do it with the following code but the problem is I have to remove it from one canvas first. Code is:
private void add_template_Click(object sender, RoutedEventArgs e)
{
var childrenList = Template_canvas1.Children.Cast<UIElement>().ToArray();
root.Children.Clear();
foreach (var c in childrenList)
{
Template_canvas1.Children.Remove(c);
root.Children.Add(c);
}
}
I want to keep these elements on both the canvas. Is there another way?
Instead of trying to add the same Template_canvas1.Children to the root canvas, first make a copy of those Children and then add the copy to the root canvas.
public static T CloneXaml<T>(T source)
{
string xaml = XamlWriter.Save(source);
StringReader sr = new StringReader(xaml);
XmlReader xr = XmlReader.Create(sr);
return (T)XamlReader.Load(xr);
}
Then change your loop to:
foreach (var c in childrenList)
{
var copy = CloneXaml(c);
root.Children.Add(copy);
}
I haven't tested this code, so you may have to modify it a bit, but it should put you in the right direction.
Alternatively, you can probably use the code below which is copied from Dr Herbie's answer:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using System.Reflection;
using Windows.UI.Xaml.Controls;
namespace UIElementClone {
public static class UIElementExtensions {
public static T DeepClone<T>(this T source) where T : UIElement {
T result; // Get the type
Type type = source.GetType(); // Create an instance
result = Activator.CreateInstance(type) as T;
CopyProperties<T>(source, result, type);
DeepCopyChildren<T>(source, result);
return result;
}
private static void DeepCopyChildren<T>(T source, T result) where T : UIElement {
// Deep copy children.
Panel sourcePanel = source as Panel;
if (sourcePanel != null) {
Panel resultPanel = result as Panel;
if (resultPanel != null) {
foreach (UIElement child in sourcePanel.Children) {
// RECURSION!
UIElement childClone = DeepClone(child);
resultPanel.Children.Add(childClone);
}
}
}
}
private static void CopyProperties<T>(T source, T result, Type type) where T : UIElement {
// Copy all properties.
IEnumerable<PropertyInfo> properties = type.GetRuntimeProperties();
foreach (var property in properties) {
if (property.Name != "Name") { // do not copy names or we cannot add the clone to the same parent as the original.
if ((property.CanWrite) && (property.CanRead)) {
object sourceProperty = property.GetValue(source);
UIElement element = sourceProperty as UIElement;
if (element != null) {
UIElement propertyClone = element.DeepClone();
property.SetValue(result, propertyClone);
}
else {
try {
property.SetValue(result, sourceProperty);
}
catch (Exception ex) {
System.Diagnostics.Debug.WriteLine(ex);
}
}
}
}
}
}
}
}
If none of these worked for you, I'm afraid you'd have to implement your own serializer. It looks like David Poll implemented a decent serlizer, so have a look. Using his serlizer is as simple as using the XamlWriter, then you can use the XamlReader:
public static T CloneXaml<T>(T source)
{
UiXamlSerializer uxs = new UiXamlSerializer();
string xaml = uxs.Serialize(source);
StringReader sr = new StringReader(xaml);
XmlReader xr = XmlReader.Create(sr);
return (T)XamlReader.Load(xr);
}
To get this functionality, download his Slab library, go to the "Binaries" folder and copy all the dlls that start with "SLaB.Utilities.Xaml.Serializer" to your project. There might be some other dlls required as dependency. He has example solution in the library if you like to look at the code an learn.
Without a good Minimal, Complete, and Verifiable example that shows clearly what you've tried, with a precise description of what exactly you're trying to achieve, it's impossible to know for sure what the best answer would be.
That said, given that WPF already knows how to "clone" elements in a sense, through the use of data templates, your question really sounds a lot like an XY Problem to me. That is, you only think you need to literally clone the elements already in your visual tree, when in fact what you should be doing is defining a view model that represents the data to be displayed for the element(s) to be "cloned", define a single data template that uses XAML to describe the visual elements that will display the data in the view model, and then simply apply the template as necessary wherever you want the visual elements to be "cloned".
I.e. they won't really be cloned. Instead, WPF will automatically populate a whole new sub-tree of visual elements exactly as you want them to be. Since the template allows you to completely define all aspects, there is no issue related to e.g. trying to get the event subscriptions hooked up, setting up bindings correctly, etc.
In your specific example (vague though it is), it sounds like you most likely want to use an ItemsControl element, in which the ItemsPanel is a Canvas object. You would then define a DataTemplate that represents a single item in the ItemsPanel; this template would be referenced either implicitly by setting its DataType property, or explicitly by setting the ItemsControl.ItemTemplate property. Then, instead of cloning anything, you just create an ItemsControl when you want a copy of your visual for the data.
New answer after user's feedback that it is not working on Windows Phone
Complete final Windows Phone App can be downloaded here.
There are some API differences, for example instead of pinfo.SetMethod property we have to use pinfo.GetSetMethod() etc.
Secondly, I was unknowingly didn't check for Name property which must not be copied as otherwise we would be making another instance with same name.
Third, I posted for simple case of simple controls like Button, TextBox, Rectangle etc which do not contain children. If that is the case you have to go for recursive deep cloning to clone children too. As children could have more children and so on.
foreach (UIElement oldElem in Canvas1.Children)
{
try
{
Type t = oldElem.GetType();
UIElement newElem = (UIElement)Activator.CreateInstance(t);
PropertyInfo[] info = t.GetProperties();
int i = 0;
foreach (PropertyInfo pinfo in info)
{
if (pinfo.Name == "Name") continue;
try
{
if (pinfo.GetSetMethod() != null) // avoid read-only properties
pinfo.SetValue(newElem, pinfo.GetValue(oldElem, null),null);
}
catch (Exception ex)
{
Debug.WriteLine((++i).ToString() + " : " + pinfo.ToString());
}
}
Canvas.SetLeft(newElem, Canvas.GetLeft((oldElem)));
Canvas.SetTop(newElem, Canvas.GetTop((oldElem)));
Canvas2.Children.Add(newElem);
}
catch (Exception ex)
{
}
}
And if you are going for truly deep cloning then replace code in outer try block above with a simpler:
foreach (UIElement oldElem in Canvas1.Children)
{
try
{
UIElement newElem = oldElem.DeepClone();
Canvas2.Children.Add(newElem);
Canvas.SetLeft(newElem, Canvas.GetLeft(oldElem));
Canvas.SetTop(newElem, Canvas.GetTop(oldElem));
}
catch (Exception ex){ }
}
Old answer based on WPF only
Don't know about windows phone but in WPF this creates a fresh element and puts it in exactly same place in another canvas. Check if it fits your needs, else I will update it again.
foreach (UIElement oldElem in Canvas1.Children)
{
Type t = oldElem.GetType();
UIElement newElem = (UIElement)Activator.CreateInstance(t);
PropertyInfo[] info = t.GetProperties();
int i = 0;
foreach (PropertyInfo pinfo in info)
{
try
{
if (pinfo.SetMethod != null) // avoid read-only properties
pinfo.SetValue(newElem, pinfo.GetValue(oldElem));
}
catch (Exception ex)
{
Debug.WriteLine((++i).ToString() + " : " + pinfo.ToString());
}
}
Canvas.SetLeft(newElem, Canvas.GetLeft((oldElem)));
Canvas.SetTop(newElem, Canvas.GetTop((oldElem)));
Canvas.SetRight(newElem, Canvas.GetRight((oldElem)));
Canvas.SetBottom(newElem, Canvas.GetBottom((oldElem)));
Canvas2.Children.Add(newElem);
}
I just searched for a way to enable a child control while the parent control has IsEnabled = false.
All answers that I have found up to now say that it is not possible - one has to enable the parent and disable the child controls except the ones that should still be enabled.
However, by overriding the Metadata for the IsEnabledProperty in the App.xaml.cs file, I was able to change this default behavior:
protected override void OnStartup(StartupEventArgs e)
{
UIElement.IsEnabledProperty.OverrideMetadata(typeof(FrameworkElement),
new UIPropertyMetadata(true,IsEnabledChanged, CoerceIsEnabled));
}
private void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var childrenCount = VisualTreeHelper.GetChildrenCount(d);
for (int i = 0; i < childrenCount; ++i)
{
var child = VisualTreeHelper.GetChild(d, i);
child.CoerceValue(UIElement.IsEnabledProperty);
}
}
private object CoerceIsEnabled(DependencyObject d, object basevalue)
{
var parent = VisualTreeHelper.GetParent(d) as FrameworkElement;
if (parent != null && parent.IsEnabled == false)
{
if (d.ReadLocalValue(UIElement.IsEnabledProperty) == DependencyProperty.UnsetValue)
{
return false;
}
}
return basevalue;
}
Now you can manually set the IsEnabled property on a child, which overrides the parent value.
Are there any drawbacks of this approach?
The solution that is proposed in the question may be fine in some scenarios. However, changing the default framework behavior for all UIElements in the application, could introduce compatibility issues and it might be difficult in the future to understand where/why the behavior was changed.
An alternative approach would be to keep the default behavior of the framework and only override that behavior manually in specific places, when needed. One way to do this is be creating a simple wrapper element that breaks the IsEnabled inheritance chain from the parent.
The framework's default coerce callback checks the parent IsEnabled value and inherits it. This control sets a new coerce callback that just returns the value directly without checking inheritance.
public class ResetIsEnabled : ContentControl
{
static ResetIsEnabled()
{
IsEnabledProperty.OverrideMetadata(
typeof(ResetIsEnabled),
new UIPropertyMetadata(
defaultValue: true,
propertyChangedCallback: (_, __) => { },
coerceValueCallback: (_, x) => x));
}
}
It can be used like this
<ParentControl IsEnabled="False">
<!-- Any elements here will have IsEnabled set to false, inherited from the parent -->
<ResetIsEnabled>
<!-- Any child elements here will have IsEnabled set to true (the default value) -->
</ResetIsEnabled>
</ParentControl>
This worked for my situation on a control used several times with some slight modifications.
Placing here to help any future web searchers in a similar situation:
placed it in a static constructor instead of an event, otherwise it tried to set it multiple times and threw a "PropertyMetadata is already registered for type '{type}'." exception.
Changed the type to match the control
Code:
Make sure to find and replace [CustomControl] with the type name of your control.
static [CustomControl]()
{
UIElement.IsEnabledProperty.OverrideMetadata(typeof([CustomControl]), new UIPropertyMetadata(true, [CustomControl]_IsEnabledChanged, CoerceIsEnabled));
}
private static void [CustomControl]_IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var childrenCount = VisualTreeHelper.GetChildrenCount(d);
for (int i = 0; i < childrenCount; ++i)
{
var child = VisualTreeHelper.GetChild(d, i);
child.CoerceValue(UIElement.IsEnabledProperty);
}
}
private static object CoerceIsEnabled(DependencyObject d, object basevalue)
{
var parent = VisualTreeHelper.GetParent(d) as FrameworkElement;
if (parent != null && parent.IsEnabled == false)
{
if (d.ReadLocalValue(UIElement.IsEnabledProperty) == DependencyProperty.UnsetValue)
{
return false;
}
}
return basevalue;
}
The drawback is at least, that you break the basic concept, and the IsEnabled is not used for the intended scope. This workaround also makes maintenance a bit more complex (the developer has to understand first, why it works differently).
As it is suggested in comments, I would say, that a redesign of this window would help. Especially, if I would like to forbid only the editing (data modification) in the form, I would use other properties like IsReadOnly.
Another option is to override the FrameworkPropertyMetadataOptions to remove the Inherits property. I had a similar problem with the FontSize and this worked well:
FontSizeProperty.OverrideMetadata(
typeof(YourControl),
new FrameworkPropertyMetadata(8.0,
FrameworkPropertyMetadataOptions.None, changed));
I've got a working custom markup extension which retrieves information out of the DataContext in a specific way (unimportant for this question).
All is well until I use this markup extension in elements that are not part of the visual or logical tree. In my particular example in the element InputBindings. In this scenario instead of retrieving a FrameworkElement as DependencyObject I get a Freezable (KeyBinding).
How can I access the DataContext through code?
My XAML code:
<UserControl.InputBindings>
<KeyBinding
Key="CapsLock"
Command="{wtc:CommandBinding {x:Static b:Commands.OpenTimeLine}}" />
</UserControl.InputBindings>
Code in my custom markup extension where I normally retrieve my DataContext:
protected override object ProvideValue(
DependencyObject dependencyObject,
DependencyProperty dependencyProperty )
{
if ( dependencyObject is Freezable )
{
// TODO: How to handle freezable?
}
_frameworkElement = dependencyObject as FrameworkElement;
if ( _frameworkElement == null )
{
throw new InvalidImplementationException(
"The DataContextBinding may only be used on framework elements." );
}
if ( !_dataContextChangedHooked )
{
_frameworkElement.DataContextChanged += DataContextChanged;
_dataContextChangedHooked = true;
}
return ProvideValue( _frameworkElement.DataContext );
}
The entire source code is online as well. I have quite an extensive class hierarchy for markup extensions.
AbstractMarkupExtension ⇐ AbstractDependencyPropertyBindingExtension ⇐ AbstractDataContextBindingExtension ⇐ CommandBindingExtension
One solution is surprisingly easy. Assuming the DataContext you are looking for is the same as the DataContext of your root object you can simply use the IRootObjectProvider. This provider is accessible through the IServiceProvider which is passed as an argument of ProvideValue.
var rootProvider = (IRootObjectProvider)ServiceProvider
.GetService( typeof( IRootObjectProvider ) );
_frameworkElement = rootProvider.RootObject as FrameworkElement;
There might be more complex scenarios where you have to traverse the tree (through LogicalChildren) in order to find the desired DataContext.
Here would be the nasty reflection way:
var context = (FrameworkElement)typeof(DependencyObject)
.GetProperty("InheritanceContext", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(dependencyObject, null);
var datacontext = context.DataContext;
(The cast to FrameworkElement is not safe, InheritanceContext is also of type DependencyObject, the InheritanceContext usually is the object declaring the property in which the Freezable is used, if it is not a FrameworkElement you might need to recurse)
i could need some help on something i can't figure out at this point:
I need to update XAML-Bindings (via a class to varying resx-files) on a certain event for controls with a specific name prefix. As the controls have different types and i do no know how the same page will look somewhen in the future, i'd like to do that with reflections only...
something like
var meth1 = control.GetType().GetMethod("GetBindingExpression");
var meth2 = control.GetType().GetMethod("SetBinding");
BindingExpression be = (BindingExpression)meth1.Invoke(target, null);
Binding bind = be.ParentBinding;
meth2.Invoke(target, new object[] { bind });
seems to be the right idea to me, but i cannot figure out how to get the target DependencyProperty from a DependencyObject without knowing the DependencyObject Type before...
I am pretty sure i am missing something rather easy here ...
[edit]
I know that i can go through the controls and just enter the new string i get from a ResourceManager Object into the e.g. Text-Property of the Control, but in that case i'd again have to check for Text, Header, whatever properties ... if possible, reflections only seem the cleaner way to me.
You can simply use following way,
FrameworkElement fe = control as FrameworkElement;
foreach(PropertyDescriptor pd in TypeDescriptor.GetProperties(control))
{
FieldInfo field = control.GetType().GetField(pd.Name + "Property");
if(field == null)
continue;
DependencyProperty dp = field.GetValue(control) as DependencyProperty;
if(dp == null)
continue;
BindingExpression be = control.GetBindingExpression(dp);
if(be == null)
continue;
// do your stuff here
}
Thanks to the both hints regarding the cast to FrameworkElement, i managed to get back on track:
foreach (var f in control.GetType().GetFields())
{
DependencyProperty dp = f.GetValue(control) as DependencyProperty;
if (dp != null)
{
BindingExpression be = ((FrameworkElement)control).GetBindingExpression(dp);
if (be != null)
{
// stuff here
}
}
}
From here on i think will get things done
I have a silverlight app consisting of several dialogs each with a collection of FrameworkElements in it.
Is it possible to find the dialog in which a Framework element is in?
You can use the VisualTreeHelper. The code below is what I use to find the Page in a WPF application. You can replace Page with whatever container you need in Silverlight, maybe Popup.
var parent = VisualTreeHelper.GetParent(this);
while (!(parent is Page))
{
parent = VisualTreeHelper.GetParent(parent);
}
http://forums.silverlight.net/p/55369/142519.aspx has a method to simplify the above example code and make it generic-friendly:
public static class ControlFinder
{
public static T FindParent<T>(this UIElement control) where T: UIElement
{
UIElement p = VisualTreeHelper.GetParent(control) as UIElement;
if (p != null)
{
if (p is T)
return p as T;
else
return ControlFinder.FindParent<T>(p);
}
return null;
}
}
Use it like:
var page = myElement.FindParent<Page>();
Yes it is possible. If you know the structure of your control, then you can user FrameworkElement.GetParent() or else you can use Tree-traversal algorithms like BFS or DFS to find the your framework element.