How to Set Parent Panel Alignment of The Control Programmatically? - c#

Background:
I have created a helper method to set properties of each TextBlockas per its adjacent TextBox for some scenarios in my application. In this method I already have the TextBlock object, the TextBox object and the parent object (it's always RelativePanel in my case).
The RelativePanel in my views always contain only TextBox & TextBlock.
// Helper Method
public static void SetPropertiesOfEachTextBlock(IList<TextBox> boxes)
{
foreach (TextBox textBox in boxes)
{
var parent = VisualTreeHelper.GetParent(textBox) as RelativePanel;
foreach(var element in parent.Children)
{
if(element is TextBlock)
{
TextBlock textBlock = (TextBlock)element;
textBlock.FontSize = textBox.FontSize;
textBlock.Margin = textBox.Margin;
textBlock.FontWeight = textBox.FontWeight;
textBlock.Foreground = new SolidColorBrush(Colors.Gray);
// Here I need to set alignment to the adjacent TextBox by the RelativePanel
}
}
}
}
Relative Panel Sample:
<RelativePanel>
<TextBox Name="UserName"/>
<TextBlock RelativePanel.AlignLeftWith="UserName" />
</RelativePanel>
Question:
How can I set the following property of TextBlock programmatically:
RelativePanel.AlignLeftWith="UserName"

AlignLeft is an Attached Property and can be found on RelativePanel itself. You set them like this:
RelativePanel.SetAlignLeftWith(element, UserName);
You can read the docs on the property here:
Edit: fixed syntax error based on comment

Related

Get Text of Node(Item) Treeview in SelectedItemChange Event IN WPF

First of all I am from Iran and I can't speak English very well, sorry for this.
I made something like OpenFileDialog in WinForms and it works correctly.
After that for better User Interface I'm try to make it in WPF.
I use TreeView and other controls to make it work in both platforms (Winforms and WPF).
In WPF I want to get the text of Treeview item for comparison, in Winform I could do this with below code:
private void Folder_FileTreeView_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
{
if(e.Node.Text=="Desktop")
{
//Do something
}
}
in WPF I added text with and image next to each other using this method:
public object Node(string NodeIMGUri, string NodeText)
{
Image IMG = new Image() { Source = new System.Windows.Media.Imaging.BitmapImage(new Uri(NodeIMGUri, UriKind.RelativeOrAbsolute)) };
TextBlock Text = new TextBlock() { Text = NodeText };
StackPanel CustomStackPanel = new StackPanel();
TreeViewItem TVItem = new TreeViewItem();
IMG.Height = 50;
IMG.Width = 50;
CustomStackPanel.Orientation = Orientation.Horizontal;
CustomStackPanel.Children.Add(IMG);
CustomStackPanel.Children.Add(Text);
TVItem.Header = CustomStackPanel;
return TVItem;
}
But when in SelectedItemChanged (or ItemChanged) event of TreeView how can I get the text of the item clicked?
If anyone can help me to complete this dll, I can send it free to all programmers.
This dll supports most languages like german, france, china, hindi, bengali, indonesian, persian, japanese, korean, arabic, portuguese, latin, swede, english
The way you are currently doing things, you would need to go through the children of your item to find the TextBlock and get the Text property from that. But this isn't the proper or recommended way of doing things in WPF.
Instead of manually creating TreeViewItems, you shoudl be using TreeView.ItemsSource and TreeView.ItemTemplate. If you're not familiar with how to use DataTemplates in WPF, you should really read up on it. Here is a good place to start.
Basically you would define a class, let's say Folder, then you would have a collection of Folder objects (e.g. List<Folder>), and you would bind that to TreeView.ItemsSource. You would then use a DataTempalte to declare the visual representation of how a Folder object should look in the TreeView. Then, when the selected item is changed, you can use TreeViewItem.DataContext to get the Folder object that is being selected, which would probably have a property such as Path.
So what I can see there you put into your TreeViewItem's header a panel with two items - Image and TextBlock with Text you want to get. The TextBlock is stored as the second item in the panel's collection (Children). All you have to do is this:
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
CustomStackPanel panel = (CustomStackPanel)((TreeViewItem) e.NewValue).Header;
TextBlock textBlock = (TextBlock)panel.Children[1];
string text = textBlock.Text; //Your text
}
Hope it helps.

How do you get the actual height of the DropDownList attached to a ComboBox in WPF?

I want to get the actual height of the ComboBox plus the height of its DropDown when it is opened. However there does not appear to be an accessible property on the ComboBox to give this.
The ComboBox.ActualHeight property only gives the height of the base ComboBox, not its dropdown.
This one is kind of infuriating as I think I can see the value set on a property in the debugger, but the property is not accessible in code for some reason - ItemsHost.ActualHeight.
See below:
However, ItemsHost which appears to have the height of the dropdown is not accessible from code!
The property is not accessible because it is not defined as public. You could get its value using reflection:
Get property value from string using reflection in C#
You shouldn't really do this though as the property is non-public for a reason.
Instead, you could get a reference to the Popup element of the ComboBox and check the ActualHeight property of its Child, e.g.:
ComboBox cmb = sender as ComboBox;
double heightOfComboBox = cmb.ActualHeight;
double heightOfPopup = 0.0;
Popup popup = cmb.Template.FindName("PART_Popup", cmb) as Popup;
if (popup != null)
{
FrameworkElement fe = popup.Child as FrameworkElement;
if (fe != null)
heightOfPopup = fe.ActualHeight;
}
double totalHeight = heightOfComboBox + heightOfPopup;

Datepicker column enabling itself when playing with vertical scrollbar in WPF

I have a weird bug and i need some rescue.
I have a grid with several column of multiple types in WPF.
One or several of these columns are DatePickers that I created through a FrameElementFactory :
FrameworkElementFactory dateFactory = new FrameworkElementFactory(typeof(DatePicker));
...
column = new DataGridTemplateColumn { CellTemplate = new DataTemplate
{ VisualTree = dateFactory } };
this._mainDatagrid.Columns.Add(column);
I have put a method to disable the DatePickers of my grid on a certain state of one of my variable:
private IEnumerable<DataGridRow> GetDataGridRows(DataGrid grid)
{
//return the Datagrid Rows
}
public void SetChangeLockState(bool isUnlocked)
{
IEnumerable<DataGridRow> _rows = this.GetDataGridRows(this._mainDatagrid);
foreach (DataGridColumn _column in this._mainDatagrid.Columns)
{
if (_column.GetType() != typeof(DataGridTemplateColumn)) continue;
foreach (DataGridRow _row in _rows)
{
FrameworkElement frameworkElement = _column.GetCellContent(_row);
if (frameworkElement != null) frameworkElement.IsEnabled = !isUnlocked;
}
}
}
The problem is that when I am playing with the elevator of my grid, the Datepicker keep enabling and disabling for no reason.
Example:
All my DatePicker are enabled, I am playing with my vertical scroll bar, no problem.
All my DatePickers are Disabled, I am playing with my vertical scroll bar.
1 Datepicker will suddenly appear enable :
DatePicker enabled 1
I am keeping playing with the scrollbar and another Datepicker will go enabled :
DatePicker enabled 2
Have you any idea of what could happen ?
Thanks for your help.
This will be because DataGrid.EnableRowVirtualization defaults to true. This enables UI virtualisation, which means that UI Elements that are scrolled out of view may be disposed or reused. Thus on occasions when scrolling an item back into view a new DatePicker will be created via your factory, and of course this new DatePicker will not have existed when SetChangeLockState was called and thus will not be disabled.
A quick fix would be to set DataGrid.EnableRowVirtualization to false, but this may not be very performant if you have lots of rows. A better solution would be to bind rather than set the IsEnabled property, e.g. to a property on your Window using RelativeSource.
Thanks to Ben, here is the code :
dateFactory.SetBinding(
//My IsEnabled property I wanted to change
IsEnabledProperty,
new Binding("IsLockedDatagrid")
{
//Datagridwidget is the datagrid I am using where I can found the IsLockedDatagrid boolean variable (in my xaml)
RelativeSource =
new RelativeSource(RelativeSourceMode.FindAncestor, typeof(DataGridWidget), 1),
Mode = BindingMode.OneWay,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
});

Can't find element that should exist?

I am adding tabs to my tab control through code:
TabItem tab = new TabItem();
var stack = new StackPanel() { Orientation = Orientation.Horizontal };
stack.Children.Add(new TextBlock() { Text = header });
stack.Children.Add(new TextBlock() { Name = "extra" });
tab.Header = stack;
tabControl.Items.Add(tab);
As you can see, it creates the header of the tabItem with a stack panel. It adds two text blocks; one of which is empty, but I've assigned the name "extra". What I would like to do is, later in the code, edit the textBlock named "extra" and add some new text to it.
How would I find and edit this element? I have tried the following code, but its producing an error saying the element can not be found:
object test = Application.Current.FindResource("extra");
FindName is what you are looking for but your TextBlock is not in the correct WPF Namescope.
MSDN states:
If you add an object to an object tree at a point in time after the XAML that produced that tree was parsed, a Name or x:Name value on the new object does not automatically update the information in a XAML namescope. To add a name for an object into a WPF XAML namescope after XAML is loaded, must call the appropriate implementation of RegisterName on the object that defines the XAML namescope.
For example:
var textBlock = new TextBlock() { Name = "extra" };
stack.Children.Add(textBlock );
RegisterName(textBlock);
...
TextBlock textBlock = FindName("extra") as TextBlock;
Finally, Application.Current.FindResource("extra") is returning null because the element does not exist when project resources are created. More on FindResource.
Just use FrameworkElement.FindName method:
var control = tab.FindName("extra");
if(control is TextBlock){
// your logic here
}
You don't need Application.Current.Resource dictionary here because it's different collection. If you want to use it then you should put user controls within Resource dictionary beforehand.
Because you are trying to find resource with the key "extra". It's wrong.
Try this:
TabItem tab = new TabItem();
var stack = new StackPanel() { Orientation = Orientation.Horizontal };
var textBlock = new TextBlock() { Name = "extra" }
stack.Children.Add(new TextBlock() { Text = header });
stack.Children.Add(textBlock);
tab.Header = stack;
tabControl.Items.Add(tab);
Now you can reach it with textBlock instance.
Here's some VB WPF code for what you need
Dim test As TextBlock
test = DirectCast(FindName("extra"), TextBlock)
I have no idea if it will work like that in C# WPF although if that doesn't work try looking up CType

How To select and object of label , If it is a child of Grid in C#

I have a program in which I have to change text of label (on the click of button) which is a child of a grid
public class XLabel
{
Grid uiGrid = null;
TextBlock textblock = null;
string emptyString = "";
Public void createLabel()
{
uiGrid.Children.Add(textblock);
grid.Children.Add(uiGrid);
}
public void cleartext()
{
textblock.Text = emptyString;
}
}
In other class I have a method to clear text
public void clearText()
{
XLabel obj = new XLabel();
obj.cleartext(indexi);
}
How to select specific label to clear text from specific grid if there are many grids and each having one label .
The Grid object has properties like Name or Tag, that can be used for searching.
If you create grids programmatically, you should create a unique property for each, then in your clearText method you just receive all Grid objects from XLabel object and search for the one with proper name/tag.
To get a list of labels from grid, you could use lambda like that:
List<UIElement> list =
YourGrid.Children.Where(o => o.GetType() == typeof(Label)).ToList();
To extend Olter's answer,
Create your Textblock and Grid like this
Grid uiGrid = new Grid() { Name = "uiGrid"+1 };
TextBlock textblock = new TextBlock() { Name = "textBlock"+1 };
Each time change the number you add to the grid and textblock and somehow plan to keeptrack of that number.
Then when you want to clear the text,
(this.FindName("textBlock"+1) as TextBlock).Text = "";

Categories