i'm starting with WPF and i have a problem. Please help me. Thanks, sorry for my bad English!
I have added ContentControl to a Canvas, and i want to remove/delete it.
Draw ContentControl code:
ContentControl cc = new ContentControl();
cc.Content = shape;
cc.Height = h;
cc.Width = w;
Style s = myCanvas.FindResource("DesignerItemStyle") as Style;
cc.Style = s;
Canvas.SetLeft(cc, x);
Canvas.SetTop(cc, y);
myCanvas.Children.Add(cc);
I use HitTest to remove it but i can remove only shape
private void myCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
Point pt = e.GetPosition((Canvas)sender);
HitTestResult result = VisualTreeHelper.HitTest(myCanvas, pt);
if (result != null)
{
myCanvas.Children.Remove(result.VisualHit as Shape); //it works with shape
// i have changed it into myCanvas.Children.Remove(result.VisualHit as ContentControl);
//but it didn't work with ContentControl
}
}
It is so because the ContentControl is the parent of the Shape, and the Canvas's children contains the ContentControl that hosts the shape.
You could do this to fix your issue :)
private void myCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
DependencyObject ob = FindAncestor<ContentControl>((DependencyObject)e.Source);
if (ob.GetType() == typeof(ContentControl))
myCanvas.Children.Remove(ob as ContentControl);
}
public T FindAncestor<T>(DependencyObject dependencyObject)
where T : DependencyObject
{
var parent = VisualTreeHelper.GetParent(dependencyObject);
if (parent == null) return null;
var parentT = parent as T;
return parentT ?? FindAncestor<T>(parent);
}
I suggest you the next solution:
private void myCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
var dObj = sender as DependencyObject;
if(dObj == null) return;
var ob = dObj.GetChildOfType<ContentControl>();
if (ob != null)
{
myCanvas.Children.Remove(ob);
}
}
Helper code:
public static T GetChildOfType<T>(this DependencyObject depObj)
where T : DependencyObject
{
if (depObj == null) return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null) return result;
}
return null;
}
just put the helper inside the public static class as it is an extension method
regards
You can add the control, for example:
Rectangle r = new Rectangle() { Name = "MyName" };
and to remove from the canvas:
UIElement element = StationLayout.FindName ("MyName") as UIElement;
StationLayout.Children.Remove(element);
Related
I am trying to create a UWP control that is a combobox but whose width stays constant no matter which item is selected. (The default ComboBox will resize itself every time a new item is picked, which may cause the layout of the rest of the page to change.)
I am basing my control off of one that I wrote for WPF, which works fine. This is the code for the UWP version:
using Windows.UI.Xaml.Controls;
using Windows.Foundation;
using System.Reflection;
namespace Foo
{
public sealed class ConstantWidthComboBox : ComboBox
{
private readonly double _emptyWidth;
public ConstantWidthComboBox()
{
this.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
_emptyWidth = this.DesiredSize.Width;
}
protected override void OnItemsChanged(object e)
{
base.OnItemsChanged(e);
AdjustWidth();
}
private void AdjustWidth()
{
double maxItemWidth = 0;
foreach (var item in Items)
{
if (item is ComboBoxItem cmbItem)
{
cmbItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
if (cmbItem.DesiredSize.Width > maxItemWidth)
maxItemWidth = cmbItem.DesiredSize.Width;
}
else
{
object content = item;
if (!string.IsNullOrEmpty(this.DisplayMemberPath))
{
content = EvaluateDisplayMemberPath(item, this.DisplayMemberPath);
}
ContentPresenter presenter = new ContentPresenter() { Content = content };
presenter.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
if (presenter.DesiredSize.Width > maxItemWidth)
maxItemWidth = presenter.DesiredSize.Width;
}
}
this.Width = maxItemWidth + _emptyWidth + this.Padding.Left + this.Padding.Right;
}
private object EvaluateDisplayMemberPath(object item, string path)
{
if (item == null)
return null;
if (string.IsNullOrEmpty(path))
return item;
int k = path.IndexOf(".");
string property = (k == -1) ? path : path.Substring(0, k);
string trailer = (k == -1) ? null : path.Substring(k + 1);
object value = item.GetType().GetProperty(property, BindingFlags.Public | BindingFlags.Instance).GetValue(item, null);
return EvaluateDisplayMemberPath(value, trailer);
}
}
}
(Note that this works if the combobox consists of ComboBoxItems or if the DisplayMemberPath is used to get the text to display. I haven't tested it on wider circumstances such as if the items are data templates or whatnot.)
While this works in WPF, in UWP the presenter's DesiredSize.Width value is always zero.
Does anyone know how to measure the (hypothetical) width of a control that is not actually part of the UI layout in UWP, such as for the ContentPresenter in the above code?
Please find the ContentPresenter control through Visual Tree, then access the ActualWidth property.
You can follow the first answer of this question edited by Martin Zikmund to do this like following.
private void Page_Loaded(object sender, RoutedEventArgs e)
{
var contentPresenter = FindChild<ContentPresenter>(MyConstantWidthComboBox);
double k = contentPresenter.ActualWidth;
}
public T FindChild<T>(DependencyObject parent)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T typedChild)
{
return typedChild;
}
var inner = FindChild<T>(child);
if (inner != null)
{
return inner;
}
}
return default;
}
I am working on a project where i have to deal with navigation based on Frame in MVVM pattern thus to get to the element Name x:Name of type Frame we have to convert MainWindow into DependencyObject like this..
private static FrameworkElement GetDescendantFromName(DependencyObject parent, string name)
{
var count = VisualTreeHelper.GetChildrenCount(parent);
if (count < 1)
{
return null;
}
for (var i = 0; i < count; i++)
{
var frameworkElement = VisualTreeHelper.GetChild(parent, i) as FrameworkElement;
if (frameworkElement != null)
{
if (frameworkElement.Name == name)
{
return frameworkElement;
}
frameworkElement = GetDescendantFromName(frameworkElement, name);
if (frameworkElement != null)
{
return frameworkElement;
}
}
}
return null;
}
In Navigation Service Class i use...
var frame = GetDescendantFromName(Application.Current.MainWindow, "FrameName") as Frame;
frame.source = new Uri("Views/StudentView.Xaml");
This technique is limited to only MainWindow. When i pass new instence of EmployeeDetailView.Xaml as a depenecy Object, The Xaml File is not loaded and GetChildrenCount() returns 0.
var frame = GetDescendantFromName(EmployeeDetaiView.Xaml, "FrameName") as Frame;
here frame has null value.
how could i make it work with currently rendered EmployeeDetailView to get the Frame element?
Use Application.LoadComponent Method (Uri)
Page p = (Page) Application.LoadComponent(new Uri("Views/EmployeeDetaiView.Xaml.xaml", UriKind.Relative));
var ctrl = GetDescendantFromName(p, "SomeControl");
Here, EmployeeDetaiView.Xaml lies in Views folder.
EDIT #1 after user comments
However, as pointed by OP in his comment that VisualTreeHelper.GetChildrenCount() is returning 0. This happens because p is not part of VisualTree, once it is part of VisualTree it will work correctly.
Page p;
private void Button_Click_1(object sender, RoutedEventArgs e)
{
p = (Page) Application.LoadComponent(new Uri("Views/EmployeeDetaiView.Xaml.xaml", UriKind.Relative));
Frm.Content = p;
// This will print 0
int i = VisualTreeHelper.GetChildrenCount(p);
System.Diagnostics.Debug.WriteLine("Children count = " + i);
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
// Now it will correctly print 1, as 'p' is now part of VisualTree
int i = VisualTreeHelper.GetChildrenCount(p);
System.Diagnostics.Debug.WriteLine("Children count = " + i);
}
i am new in windows phone development and i try to Load more items into LonListSelector when scrolled to bottom in windows phone 8.
I use this code but scrollViewer throw null value exception.
ScrollViewer scrollViewer;
private void Mylist_Loaded(object sender, RoutedEventArgs e)
{
//get the ScrollViewer from the ListBox
scrollViewer = GetScrollViewer(this.MyLongListSelector);
//attach to custom binding to check if ScrollViewer verticalOffset property has changed
var binding = new Binding("VerticalOffset") { Source = scrollViewer };
var offsetChangeListener = DependencyProperty.RegisterAttached(
"ListenerOffset",
typeof(object),
typeof(UserControl),
new PropertyMetadata(OnScrollChanged));
scrollViewer.SetBinding(offsetChangeListener, binding);
}
// method to pull out a ScrollViewer
public static ScrollViewer GetScrollViewer(DependencyObject depObj)
{
if (depObj is ScrollViewer) return depObj as ScrollViewer;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = GetScrollViewer(child);
if (result != null) return result;
}
return null;
}
public void OnScrollChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
//We have to check if the values are 0.0 because they are both set to this when the scrollviewer loads
if ((scrollViewer.ScrollableHeight <= scrollViewer.VerticalOffset)
&& (scrollViewer.ScrollableHeight != 0.0 && scrollViewer.VerticalOffset != 0.0))
{
//The ScrollBar is at the bottom, load more results.
}
}
Please help me.
I'm using VisualTreeHelper.GetChildrenCount() to find child controls, but it always return 0.
Here is my code
<ScrollViewer x:Name="scrollViewerChannelsRecordTimeData">
<StackPanel x:Name="channelsRecordTimeData">
<ItemsControl x:Name="channelRecordTimeItems" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="hoursLines">
//Some Controls here
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
C# code:
channelRecordTimeItems.ItemContainerGenerator.StatusChanged += ChannelRecordTimeItemsStatusChangedEventHandler;
private void ChannelRecordTimeItemsStatusChangedEventHandler(Object sender, EventArgs e)
{
if (channelRecordTimeItems.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
if (channelRecordTimeItems.HasItems)
{
DependencyObject dependencyObject = null;
Grid gridHighlightRecordData = null;
for (int i = 0; i < channelRecordTimeItems.Items.Count; i++)
{
dependencyObject = channelRecordTimeItems.ItemContainerGenerator.ContainerFromIndex(i); //dependencyObject != null
if (dependencyObject != null)
{
Grid hoursLines = FindElement.FindChild<Grid>(dependencyObject, "hoursLines"); //hoursLines = null
}
}
}
}
}
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent); //Return 0 here
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
VisualTreeHelper.GetChildrenCount() always return 0,
The code for constructing for items here
List<ChannelRecordTimeItemData> listChannelRecordTimeItemData = new List<ChannelRecordTimeItemData>();
for(int i = 0; i < 5; i++)
{
ChannelRecordTimeItemData item = new ChannelRecordTimeItemData();
listChannelRecordTimeItemData.Add(ChannelRecordTimeItemData);
}
channelRecordTimeItems.ItemsSource = listChannelRecordTimeItemData;
channelRecordTimeItems.Items.Refresh();
I have searched on forum and internet, but i can not solve it, someone can help me?
Many thanks!
T&T
The problem is that when the ItemContainerGenerator signals the ContainersGenerated status, the container (a ContentPresenter) has been created, but not yet loaded. Especially the data template has not yet been applied to the ContentPresenter, hence there is nothing in the visual tree.
You may get around this by adding a Loaded event handler when looping over the generated containers.
private void ItemContainerGeneratorStatusChanged(object sender, EventArgs e)
{
if (itemsControl.ItemContainerGenerator.Status
== GeneratorStatus.ContainersGenerated)
{
var containers = itemsControl.Items.Cast<object>().Select(
item => (FrameworkElement)itemsControl
.ItemContainerGenerator.ContainerFromItem(item));
foreach (var container in containers)
{
container.Loaded += ItemContainerLoaded;
}
}
}
private void ItemContainerLoaded(object sender, RoutedEventArgs e)
{
var element = (FrameworkElement)sender;
element.Loaded -= ItemContainerLoaded;
var grid = VisualTreeHelper.GetChild(element, 0) as Grid;
...
}
If your using Caliburn.Micro this will help you.
For your Viewmodel the base Class should be Screen then only VisualTreeHelper.GetChildrenCount() give no.of childs.(because Screen will Activate all childs
or
otherwise (FrameworkElement)YourParent).ApplyTemplate() method
I have a reference to a GridViewColumn in my xaml code as RuntimeColumn but I am not able to disable it or set it to readonly programmatically. I will need to do this at runtime without databinding.
I tried:
this.RuntimeColumn.IsEnabled = false;
this.RuntimeColumn.ReadOnly = false;
Any ideas?
You will have to set an EventSetter with Loaded Event, and in your code behind put the following
private void GridViewColumnHeader_Loaded(object sender, RoutedEventArgs e)
{
GridViewColumnHeader columnHeader = sender as GridViewColumnHeader;
Border HeaderBorder = columnHeader.Template.FindName("HeaderBorder", columnHeader) as Border;
if (HeaderBorder != null)
{
HeaderBorder.Background = HeaderBorder.Background;
}
Border HeaderHoverBorder = columnHeader.Template.FindName("HeaderHoverBorder", columnHeader) as Border;
if (HeaderHoverBorder != null)
{
HeaderHoverBorder.BorderBrush = HeaderHoverBorder.BorderBrush;
}
Rectangle UpperHighlight = columnHeader.Template.FindName("UpperHighlight", columnHeader) as Rectangle;
if (UpperHighlight != null)
{
UpperHighlight.Visibility = UpperHighlight.Visibility;
}
Thumb PART_HeaderGripper = columnHeader.Template.FindName("PART_HeaderGripper", columnHeader) as Thumb;
if (PART_HeaderGripper != null)
{
PART_HeaderGripper.Background = PART_HeaderGripper.Background;
PART_HeaderGripper.Cursor = System.Windows.Input.Cursors.Arrow; // override the size curser
}
}