I have a multiple textblocks on my usercontrol Layoutroot the problem is how can I find a particular TextBlock by its name?
Thanx
var myElement =
((FrameworkElement)System.Windows.Application.Current.RootVisual)
.FindName("TextBlockName");
should work in this case, if the textblock has already been rendered.
To be able to easily traverse the visual tree more generally like #ColinE mentioned, you can also use the Silverlight toolkit.
// requires System.Windows.Controls.Toolkit.dll
using System.Windows.Controls.Primitives;
var myElement = myRoot.GetVisualDescendants().OfType<TextBlock>()
.Where(txb => txb.Name == "TextBlockName").FirstOrDefault();
If you are creating a UserControl, any element that you name via x:Name should be available to you as a field in your code-behind.
If you are not creating a UserControl, you can search the visual tree via Linq to VisualTree ...
TextBlock block = LayoutRoot.Descendants<TextBlock>()
.Cast<TextBlock>()
.SingleOrDefault(t => t.Name == "TextBlockName");
hey Masn i was write some code and similiar conditions in my case that all ok.
this is the case (have many listbox and named variables diferenciated by number in final the name Example: listAttachment1,listAttachment2,listAttachment3,..,etc). To best explication show my code:
public void refreshAttachmentList(ListlistOfControlsRequest, int identifier)
{
string valueName = "attachmentsField_"+identifier;
var mylistAttachment = ((FrameworkElement)System.Windows.Application.Current.RootVisual).FindName(valueName);
ListBox listAttachRequest = mylistAttachment as ListBox;
listAttachRequest.ClearValue(ItemsControl.ItemsSourceProperty);
listAttachRequest.ItemsSource = listOfAttachmentsControls;
listAttachRequest.....all properties
}
Related
I'd like to be able to set properties for various controls in my WPF application where I have the string name of a control and the string name of its type, but I don't know how to do it. So far I have this:
( (TabItem)this.FindName( "tabPatient" ) ).IsEnabled = false;
I can iterate through a list of control names and set properties with just the string name of the control, but what I want is to be able to do it without having to perform an explicit hard-coded cast of the object type.
Is there a way to do this?
Thanks.
The type does not really matter, right? All you need is a property, so you could do something like this:
var obj = FindName("name");
obj.GetType().GetProperty("IsEnabled").SetValue(obj, false);
Alternatively you could use dynamic, which does about the same thing:
dynamic dynObject = (dynamic)FindName("name");
dynObject.IsEnabled = false;
You can navigate WPF's VisualTree to find an element by name and set a property.
For example, using some helper classes found here you can say
foreach(var s in controlList)
{
var ctrl = VisualTreeHelpers.FindChild<UIElement>(this, s);
if (ctrl != null)
ctrl.IsEnabled = false;
}
You don't really need to know the control type. All controls with an IsEnabled property are based off of UIElement, so just cast the control as a UIElement to modify it's IsEnabled property
I'm very new to WPF and don't know how to do this. I have a text box in a tab item on a tab control. How can I programmatically (C#) determine what tab item is the parent of this text box? I would also like to determine what tab control is the parent of the tab item.
Thanks very much.
TabItem.Parent will provide the logical parent element of the TabItem; which will be the associated TabControl. You can use the same approach for any control with the TabItem.
((FrameworkElement)myTextBox.Parent).Parent;
If the item is deeper in the tree and becomes unknown in its depth you will need to begin to approach it in a recursive manner.
You can use FrameworkElement.Parent to walk up the hierarchy of a control in WPF. This should let you (recursively) walk up until you find the TabItem, then walk up to the TabControl from there.
I am newbie in WPF too, but what about cycle searching?
For example:
TextBox TB = new TextBox();
TabControl MyTabControl = new TabControl();
// ...
foreach (TabItem ti in MyTabControl.Items)
if (TB.Parent == ti)
{
// textbox is here!
MessageBox.Show(ti.ToString());
break;
}
Here is a generic method for finding parent controls: How can I find WPF controls by name or type?
You can call it like this:
TabItem owner = UIHelper.FindVisualParent<TabItem>(myTextBox);
I have a pivot control:
PivotItem sectionPivot = new PivotItem()
{
Header = sect.Name,
Content = new StackPanel()
};
How can I add content to it?
Edit: The reason I'm doing it programmatically is because I don't know how many pivots there will be or what they will contain until runtime. Is there some way to do that in XAML?
If you're going to do it all programatically, just add stuff to the stack panel you just created.
var panel = new StackPanel();
panel.Children.Add(new TextBlock() { Text = "Hello" });
PivotItem sectionPivot = new PivotItem()
{
Header = sect.Name,
Content = panel;
};
I typed all that without doing any checking, but hypothetically that should work...
Another answer from me. The OP added info to the question that they don't know how many there could be, and if you could still do it in XAML.
Yes, you can.
The Pivot control has an ItemsSource property, and you could bind that to something in your class that is being populated dynamically.
<controls:Pivot Title="MY APPLICATION" ItemsSource="{Binding MyPivotItemsSource}" />
Each item in that source would end up as a pivotitem. You'd also have to set up templates and stuff, so its still a lot of work...
I'm using DataGrid from CodePlex.
[Summary: How to use bind dynamic data (a property of an object specified by dynamic name) when using template selectors? Alternative solutions would be accepted as well. I've been thinking about making my own based on BoundColumn, but someone was having problems with that approach.]
I have objects (ICustomTypeDescriptor) that have properties 'name' 'description' 'c' 'd' and so on (the names are dynamic, and so is the value) and they have information behind them.
I have templates for different types. And if I have to list the properties of a single element I can just bind to the values in xaml and use datatemplateselector (in datagridtemplatecolumn) which will pick the correct template.
However, when I'm listing several of the elements, how can I use the selectors and the templates? How do I get the correct property bound to the template (two-way)?
When ignoring the need for different templates, using DataGridTextColumn with bindings is very simple (and works, see example below), is there any way to use similar approach with the DataGridTemplateColumn?
foreach (String propertyName in listOfPropertyNames)
{
DataGridTextColumn textColumn = new DataGridTextColumn()
{
Header = propertyName,
Binding = new Binding()
{
Path = new PropertyPath(propertyName)
}
};
DataGrid.Columns.Add(textColumn);
}
Found a post by Wang, Jie which helped to solve my problem.
Adding a name property to DataGridTemplateColumn and overriding GenerateElement and GenerateEditingElement and changing the default binding to the name property (within the generation) does the trick.
Now I can set the propertyname in the columns and the datatemplates (and selectors) work as expected.
See Social.MSDN for details if needed.
I have a WPF application which runs as a excel plugin, it has its visual tree like so
Excel
ElementHost
WPF UserControl
WPF ribbon bar control
Now any controls sitting on the WPF ribbon bar control are not enabled when the plugin is loaded within excel. See error below
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1''. BindingExpression:Path=IsActive; DataItem=null; target element
is 'Ribbon' (Name=''); target property is 'NoTarget' (type 'Object')
If I nest the ribbon bar control in a standalone Window(outside excel) it works fine.
Is there a way to intercept the FindAncestor call for a Window and wire it to something else.? Note that I cannot change the above binding as it isn't my control.
The most direct answer
FindAncestor is processed internally by WPF and will search up the visual tree as far as it can before going anywhere else. Only when it reaches a Visual that has no visual parent will it search elsewhere, and this depends on what it reached. For example, if it hits a FrameworkContentElement it can go to the document's container. Unfortunately if the top of the visual tree is a ElementHost, it will stop, so there is no way to reroute the call.
This means that your simplest option is to replace the binding. Fortunately this is not very difficult.
How to automatically replace a binding
Here is a simple method I wrote a while back that searches through a visual tree and replaces bindings as directed by an updateFunction. If the updateFunction returns a different binding than it is passed, the binding is updated.
static void UpdateBindings(Visual visual, Func<Binding, Binding> updateFunction)
{
if(visual==null) return;
for(int i=0; i<VisualTreeHelper.GetChildrenCount(visual); i++)
UpdateBindings(VisualTreeHelper.GetChild(visual, i) as Visual, updateFunction);
for(var enumerator = visual.GetLocalValueEnumerator(); enumerator.MoveNext(); )
{
var property = enumerator.Current.Property;
var binding = BindingOperations.GetBinding(visual, property);
if(binding==null) continue;
var newBinding = updateFunction(binding);
if(newBinding!=binding)
BindingOperations.SetBinding(visual, property, newBinding);
}
}
To illustrate how this works, here is how you could write a method that replaces a specific AncestorType in all RelativeSource FindAncestor instances, as follows:
static void ReplaceFindAncestorType(Visual visual, Type fromType, Type toType)
{
UpdateBindings(visual, binding =>
binding.RelativeSource.Mode != RelativeSourceMode.FindAncestor ? binding :
binding.RelativeSource.AncestorType != fromType ? binding :
new Binding
{
RelativeSource = new RelativeSource(
RelativeSourceMode.FindAncestor,
toType,
binding.RelativeSource.AncestorLevel),
Path = binding.Path,
Mode = binding.Mode,
Converter = binding.Converter,
StringFormat = binding.StringFormat,
UpdateSourceTrigger = binding.UpdateSourceTrigger,
});
}
Note that only commonly-used properties are copied over to the new binding.
The ReplaceFindAncestorVisualType method could be used something like this:
elementHost.LayoutUpdated += (obj, e) =>
{
ReplaceFindAncestorType(elementHost, typeof(Window), typeof(ElementHost);
};
In your case this generic replace technique won't work: It will be looking for an IsActive property on your ElementHost, which does not exist. So you probably need to change more than just the RelativeSource. This means your actual code will be more like this:
elementHost.LayoutUpdated += (obj, e) =>
{
UpdateBindings(elementHost, binding =>
binding.RelativeSource.AncestorType != typeof(Window) ? binding :
new Binding
{
Source = ultimateContainingWindowOrOtherObjectHavingIsActiveProperty,
Path = new PropertyPath("IsActive"), // Put property name here
});
};
Note that the above code assumes any FindAncestor:Window binding is the one we are looking for. More conditions can be added as needed in the conditional.
Alternative solution
There is another, completely different, solution available: It is possible to actually host the content in a borderless Window and add custom code to keep this window positioned over the ElementHost so it appears to be within the other window. This is trickier than it sounds since you have to deal with things such as ActiveWindow, ForegroundWindow, Z Order, Minimized state, keyboard focus, etc. But if your needs are very simple this can be a reasonable solution.
When using the control in Excel there is no Window in the ancestry, however, perhaps you can use Snoop to find where the binding is defined, then during run-time, find the dependency object (by type) and change its property's binding expression?
Another option would be to add a custom control that inherits from Window as an ancestor, then bind that to the Excel control.