How to increase pivot header height in UWP? - c#

I currently creating apps in UWP (Universal Windows Platform). I was using pivot control and it seems I couldn't change the pivot item header height! It stays at 68 pixels. I have no idea which element should I modify to change it.
See this image for better explanation (click for full image):

I ran into the same problem but didn't want to use the code behind option.
Using the new Live Tree Debugger in Visual Studio I found that the height comes from the PivotHeaderItem default style.
If you add a copy of that style at an appropriate scope you can set the height using Xaml and you won't need any code.
I blogged about this with a sample project.

No need to copy the entire style for PivotHeaderItem, you can just override it directly in your Template for the Pivot. Here is the relevant section you need to modify from the Sample project.
<!-- While used here to remove the spacing between header items, the PivotHeaderItem template can also be used to
display custom 'selected' visuals -->
<Style TargetType="PivotHeaderItem">
<Setter Property="Height" Value="Auto" />
</Style>

I think i use same template than you.
So, to modify height i set Height on HeaderClipper (see screenshot below) on pivot style :
Hope this will help you.
UPDATE: Because pivot header height is static and can not be more than a small size, we have to set height manually after page is loaded
private void Page_Loaded(object sender, RoutedEventArgs e)
{
foreach (PivotHeaderItem phItem in FindVisualChildren<PivotHeaderItem(mainPivot))
{
phItem.Height = 110;
}
}
//Find all children
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
ref: https://social.msdn.microsoft.com/Forums/sqlserver/en-US/8c123ae3-2884-4d40-a5d1-0a22355fcd5f/uwpxamlhow-to-increase-pivot-header-height-in-uwp?forum=wpdevelop

Related

Implementing Initial sort on ListView using a Class

I used the class found at http://www.thomaslevesque.com/2009/08/04/wpf-automatically-sort-a-gridview-continued/. But it doesn't provide the ability to sort the ListView columns when the application loads. You need to click the columns before it works. My knowledge hasn't advanced enough to be able to implement this. Can anybody help me with this?
From the following ... http://www.wpf-tutorial.com/listview-control/listview-sorting/
You can add SortDescriptions to the ListView's CollectionView, thus ...
CollectionView cv = (CollectionView)CollectionViewSource.GetDefaultView(YOURLISTVIEWNAME.ItemsSource);
cv.SortDescriptions.Add(new SortDescription("COLUMNNAMETOSORT", ListSortDirection.Ascending));
so, if your ListView is called lv, and you want to sort by Name then you would have ...
CollectionView cv = (CollectionView)CollectionViewSource.GetDefaultView(lv.ItemsSource);
cv.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
In that code you linked to, there are two functions
private static void AddSortGlyph
private static void RemoveSortGlyph
I don't see any reason why you can't call AddSortGlyph manually after the add SortDescription code.
In fact there is also a public static void ApplySort function ... you could just call that instead of adding the code i first suggested!
Getting the columns can be done using the method described here ... Get GridViewColumn Header value from ListView? ... but it must be done after the window has been activated (because the headers don't exist during the window's constructor).
Ultimately, add the following code to you MainWindow.xaml.cs
private void Window_Activated(object sender, EventArgs e)
{
List<GridViewColumnHeader> headers = GetVisualChildren<GridViewColumnHeader>(DownloadList).ToList();
GridViewSort.ApplySort(DownloadList.Items, "File Name", DownloadList, headers[10]);
}
public static IEnumerable<T> GetVisualChildren<T>(DependencyObject parent) where T : DependencyObject
{
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
if (child is T)
yield return (T)child;
foreach (var descendant in GetVisualChildren<T>(child))
yield return descendant;
}
}
and add this to your Window Xaml element
Activated="Window_Activated"
eg
<Window x:Class="Mackerel_Download_Manager.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:p="clr-namespace:Mackerel_Download_Manager.Properties"
xmlns:util="clr-namespace:Wpf.Util"
Title="Mackerel Download Manager" Height="Auto" Width="Auto" Activated="Window_Activated">
I just tested it on your code ... much easier than all this guessing I've been doing!

Detect, if ScrollBar of ScrollViewer is visible or not

I have a TreeView. Now, I want to detect, if the vertical Scrollbar is visible or not.
When I try it with
var visibility = this.ProjectTree.GetValue(ScrollViewer.VerticalScrollBarVisibilityProperty)
(where this.ProjectTree is the TreeView)
I get always Auto for visibility.
How can I do this to detect, if the ScrollBar is effectiv visible or not?
Thanks.
You can use the ComputedVerticalScrollBarVisibility property. But for that, you first need to find the ScrollViewer in the TreeView's template. To do that, you can use the following extension method:
public static IEnumerable<DependencyObject> GetDescendants(this DependencyObject obj)
{
foreach (var child in obj.GetChildren())
{
yield return child;
foreach (var descendant in child.GetDescendants())
{
yield return descendant;
}
}
}
Use it like this:
var scrollViewer = ProjectTree.GetDescendants().OfType<ScrollViewer>().First();
var visibility = scrollViewer.ComputedVerticalScrollBarVisibility;
ComputedVerticalScrollBarVisibility instead of VerticalScrollBarVisibility
VerticalScrollBarVisibility sets or gets the behavior, whereas the ComputedVerticalScrollBarVisibility gives you the actual status.
http://msdn.microsoft.com/en-us/library/system.windows.controls.scrollviewer.computedverticalscrollbarvisibility(v=vs.110).aspx
You cannot access this property the same way you did in your code example, see Thomas Levesque's answer for that :)
Easiest approach I've found is to simply subscribe to the ScrollChanged event which is part of the attached property ScrollViewer, for example:
<TreeView ScrollViewer.ScrollChanged="TreeView_OnScrollChanged">
</TreeView>
Codebehind:
private void TreeView_OnScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (e.OriginalSource is ScrollViewer sv)
{
Debug.WriteLine(sv.ComputedVerticalScrollBarVisibility);
}
}
For some reason IntelliSense didn't show me the event but it works.

How does one get a ListView (GridView) cell's contents when clicked with a mouse

I am trying to get the text value of a "cell" inside of a GridView that is set as the view of a ListView. I do not want to get the SelectedItem of the ListView as that just returns my entire View Model (but not which property the cell refers to).
I am able to get the text value by responding to direct mouse events (up down or whatever) and if the value is a textblock, obviously I can use the text. This works great and as of right now this is my only solution, although its currently limited.
I would like to take it a step further and be able to click anywhere with in the cell area, navigate around to find the appropriate textblock and then use that value. I have tried a half million ways to do this but what seems logical doesn't seem to quite work out like it should.
Setup:
I have a dynamic GridView that creates its own columns and bindings based on data models that I pass to it. I am using a programmatic cell template (shown below) to have individual control over the cells, particularly so I can add a "border" to it making it actually separate out each cell. I have named the objects so I can access them easier when I'm navigating around the VisualTree.
Here is the Template Code. (Note that the content presenter originally was a textblock itself, but this was changed for later flexibility)
private DataTemplate GetCellTemplate(string bindingName)
{
StringBuilder builder = new StringBuilder();
builder.Append("<DataTemplate ");
builder.Append("xmlns='http://schemas.microsoft.com/winfx/");
builder.Append("2006/xaml/presentation' ");
builder.Append("xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' ");
builder.Append("xmlns:local = 'clr-namespace:XXXXXXXX");
builder.Append(";assembly=XXXXXXXXX'>");
builder.Append("<Border Name=\"border\" BorderThickness=\"1,0,0,0\" BorderBrush=\"Gray\" Margin=\"-6,-3,-6,-3\">");
builder.Append("<Grid Margin=\"6,3,6,3\">");
builder.Append("<ContentPresenter Name=\"content\" HorizontalAlignment=\"Stretch\" Content=\"{Binding ");
builder.Append(string.Format("{0}", bindingName));
builder.Append("}\"/>");
builder.Append("</Grid>");
builder.Append("</Border>");
builder.Append("</DataTemplate>");
DataTemplate cellTemplate= (DataTemplate)XamlReader.Parse(builder.ToString());
return cellTemplate;
}
What I have Tried:
The logical approach for me was to react to a Mouse event. From the object that had the mouse event I would do either
A. Look at its children to find a textblock, or
B. Get its parent then look for child with a textblock.
My assumption is that if I click in white space I'm clicking in a container that has my textblock. So far the two things that come up are a Border and a Rectangle (if I don't click the text itself). A. Returns absolutely nothing except for the recangle and the border. When I do B i can find textblocks but they are every single text block in the entire row.
So what I try to do from that is get all textblocks, then go backwards till I find which one has a IsMouseOver property as true. It turns out none of these objects EVER have a IsMouseOver except the content presenter for the entire row. So this seems to indicate to me is that the whitespace in the cells does not actually contain the textblock.
What I find is that when I click on the Border and start looking at children, I eventually get to a container that has a rectangle (the rectangle I click) and a grid row view presenter. The presenter shows all of the objects inside the row (hence why i would get all textblocks when i do this recursive scan).
Here is some of the code used to do this to get an idea of what i'm doing. I have written about 10 different versions of this same recursive code generally attempting to find who has the Mouse over it and is related to a textbox.
private void OnPreviewMouseUp(object sender, MouseButtonEventArgs e)
{
object original = e.OriginalSource;
if (original is TextBlock)
{
this.valueTextBlock.Text = ((TextBlock)original).Text;
}
else if (original is FrameworkElement)
{
var result = GetAllNestedChildren<Border>(VisualTreeHelper.GetParent((DependencyObject)original)).Where(x => x.Name == "border").Where(x => HasAChildWithMouse(x)).ToList();
}
else
{
this.valueTextBlock.Text = string.Empty;
}
}
private bool HasAChildWithMouse(UIElement element)
{
if (element.IsMouseOver || element.IsMouseDirectlyOver)
return true;
var childCount = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < childCount; ++i)
{
var child = VisualTreeHelper.GetChild(element, i);
if (child is UIElement)
if (HasAChildWithMouse((UIElement)child))
return true;
}
return false;
}
private IEnumerable<T> GetAllNestedChildren<T>(DependencyObject obj) where T : UIElement
{
if (obj is T)
yield return obj as T;
var childCount = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < childCount; ++i)
{
var child = VisualTreeHelper.GetChild(obj, i);
foreach (var nested in GetAllNestedChildren<T>(child))
yield return nested;
}
}
private T GetObjectByTypeParentHasMouse<T>(DependencyObject obj) where T : UIElement
{
if (obj is T)
{
if ((VisualTreeHelper.GetParent(obj) as UIElement).IsMouseOver )
{
return obj as T;
}
}
var childCount = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < childCount; ++i)
{
var child = VisualTreeHelper.GetChild(obj, i);
var correctType = GetObjectByTypeParentHasMouse<T>(child);
if (correctType != null)
return correctType;
}
return null;
}
private T GetContainedType<T>(DependencyObject obj, bool checkForMouseOver) where T : UIElement
{
if (obj is T && ((T)obj).IsMouseOver)
return obj as T;
var childCount = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < childCount; ++i)
{
var child = VisualTreeHelper.GetChild(obj, i);
var correctType = GetContainedType<T>(child, checkForMouseOver);
if (correctType != null)
return correctType;
}
return null;
}
The other approach I took was to start with the TextBlock itself, find its containing parent and find out how i can navigate to the answer. I find the templateparent is the ContentPresenter (named ="content") I find the grid, and then the border. The parent of the border is a content presenter whos content is the data view model for the entire row. The parent of this contentpresenter is the grid column's presenter. This is the same one that i was navigating up to in the other one.
It would appear that the first approach objects while are contain the cell do not actually contain the textblock or the entire cell templated items. It would appear to me there is no way to go from the Border or Rectangle that is clicked, back to the actual text field.
"Long story short" is there ANY way to make this connection?
(Btw I am not willing to give up this ListView/GridView because its payoffs far outweigh this negative and I'd gladly give up on this idea to keep the rest).
I think you sjould be able to either
1) Add some kind of (toggle)button to the root of your data template, and either bind to Command and handle it on your viewmodel or bind to IsChecked/IsPressed and handle changes via data triggers or w/e on the view side.
2) Add EventTrigger to your datatemplate at some point, and handle PreviewNouseUp/Down events there via simple animations.

Setting DataGrid row height property programmatically

I have one question regarding standard WPF DataGrid in .NET 4.0.
When I try to set DataGrid grid row height programmaticaly using simple code:
private void dataGrid1_LoadingRow(object sender, DataGridRowEventArgs e)
{
e.Row.Height = 120;
}
everything goes fine till I try to resize grid row on the user interface /standard way on the side using mouse like in excel/ - then it appears grid row can't be resized. It just keep being 120. Its content by the way all goes messed up...
Like Sinead O'Connor would say: tell me baby - where did I go wrong?
You are not meant to set the Height of the row itself as it is resized via the header and such. There is a property, DataGrid.RowHeight, which allows you to do this properly.
If you need to set the height selectively you can create a style and bind the height of the DataGridCellsPresenter to some property on your items:
<DataGrid.Resources>
<Style TargetType="DataGridCellsPresenter">
<Setter Property="Height" Value="{Binding RowHeight}" />
</Style>
</DataGrid.Resources>
Or you can get the presenter from the visual tree (i do not recommend this) and assign a height there:
// In LoadingRow the presenter will not be there yet.
e.Row.Loaded += (s, _) =>
{
var cellsPresenter = e.Row.FindChildOfType<DataGridCellsPresenter>();
cellsPresenter.Height = 120;
};
Where FindChildOfType is an extension method which could be defined like this:
public static T FindChildOfType<T>(this DependencyObject dpo) where T : DependencyObject
{
int cCount = VisualTreeHelper.GetChildrenCount(dpo);
for (int i = 0; i < cCount; i++)
{
var child = VisualTreeHelper.GetChild(dpo, i);
if (child.GetType() == typeof(T))
{
return child as T;
}
else
{
var subChild = child.FindChildOfType<T>();
if (subChild != null) return subChild;
}
}
return null;
}
This works for me.
private void SetRowHeight(double height)
{
Style style = new Style();
style.Setters.Add(new Setter(property: FrameworkElement.HeightProperty, value: height));
this.RowStyle = style;
}

How to get instance of Panel that holds content of ItemsControl?

Every ItemsControl has its content stored in Panel right ? We can specify the panel to be used in XAML like this:
<ListView Name="LView">
<ListView.ItemsPanel>
<ItemsPanelTemplate >
<StackPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
My question is how to get instance of Panel that is used in the ItemsPanel property (of type ItemsPanelTemplate) of the particular ItemsControl ? For example ListView called LView from above code sample?
I cannot use Name property or x:Name, this must work for any ItemsControl even those using default ItemsPanel.
In the case its not clear please comment, I think there is very simple solution. If it seems to be complicated that's only because I cannot explain it properly.
It's a little tricky since you don't know the name of the Panel so you can't use FindName etc. This will work for most cases where an ItemsPresenter is present
private Panel GetItemsPanel(DependencyObject itemsControl)
{
ItemsPresenter itemsPresenter = GetVisualChild<ItemsPresenter>(itemsControl);
Panel itemsPanel = VisualTreeHelper.GetChild(itemsPresenter, 0) as Panel;
return itemsPanel;
}
An implementation of GetVisualChild
private static T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
However, the ItemsPanel isn't always used. See this answer by Ian Griffiths for a great explanation.
I can't provide you with working code, but have a look at
VisualTreeHelper class.
With the VisualTreeHelper class you can traverse the visual tree down to your template and panel.
protected Panel ItemsHost {
get {
return (Panel) typeof (MultiSelector).InvokeMember("ItemsHost",
BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance,
null, this, null);
}
}
This works like a charm in my ItemsControl! That said, it does have IsItemsHost="True" on the Panel inside, but might even work without it.
Trick is from this thread: Can I access ItemsHost of ItemsControl using reflection?
private object FindItemControl(ItemsControl itemsControl, string controlName, object item)
{
ContentPresenter container = itemsControl.ItemContainerGenerator.ContainerFromItem(item) as ContentPresenter;
container.ApplyTemplate();
return container.ContentTemplate.FindName(controlName, container);
}

Categories

Resources