ScrollView's Child Not Resizing Properly With Window Resize WinRT - c#

Scenario:
I am working on a Store App in WinRT/Win8.
I have a ScrollView, with a custom UserControl child inside - all as part of one "main" UserControl.
When the main UserControl (with the ScrollView -> child UserControl) in is Initialized/navigated to - even with the App width not full-screen; the UserControl is at the full width of the ScrollView - as desired. Images below:
Image 1 - main User Control Opens with Window Fullscreen
Image 2 - main User Control Opens Starts Half-Width (or any width)
The ScrollView itself is within a Grid and keeps with the full width of the App window, even when it's resized - as desired.
Issue:
The issue I'm having is that when I resize the App window horizontally, the child UserControl does not keep the same width as its parent ScrollView's.
This causes the ScrollView to then have Horizontal Scrollbars - which I do not want.
Image 3 - Window Horizontal Width Resized
I want to keep the width of the child to be bound inside the width of the ScrollView with no Horizontal Scrollbars (as in Image 2).
Markup is similar to this (I have stripped down for readability):
<Grid>
<!-- Some row/column definitions in here -->
...
<!-- A header TextBlock -->
...
<ScrollViewer x:Name="scrlTableRows" Grid.ColumnSpan="3" Grid.Row="1"
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
Padding="66,0,66,40" ZoomMode="Disabled">
<local:MyCustomUserControl Margin="0,10,0,10" HorizontalAlignment="Stretch" />
</ScrollViewer>
...
<!-- Just a button here-->
</Grid>
I have already tried setting (on the child custom UserControl):
Width="{Binding Path=ActualWidth, ElementName=scrlTableRows}"
The child is not set at the full width of the ScrollView to start with (which is what I need), and doesn't resize the width with its parent either - giving me scrollbars
Width="{Binding Path=Width, ElementName=scrlTableRows}" The child does start at the full width of the parent, but doesn't resize - giving me scrollbars
I have also tried placing the UserControl inside a Grid (within the ScrollView), amongst many other HorizontalAligment and Width properties.
All to no avail.
No other similar situations/answers have worked from other helpful fellows at StackOverflow.
Obviously, I need the vertical scrollbars - as is kind of evident; before anyone asks.
Can anybody give me any pointers, please?
Update:
Here is the custom UserControl's Xaml, as requested by #LovetoCode:
<UserControl *usual user control declaritive stuff in here*>
<UserControl.Resources>
<CollectionViewSource x:Key="FieldViewModelsSource" Source="{Binding ItemToEdit.FieldViewModels}"/>
<datatemplateselectors:FieldViewModelDataTemplateSelector
x:Key="FieldViewModelDataTemplateSelector"
AudioFieldTemplate="{StaticResource TableRowAudioFieldDataTemplate}"
CheckboxFieldTemplate="{StaticResource TableRowCheckboxFieldDataTemplate}"
DatasetFieldTemplate="{StaticResource TableRowDatasetFieldDataTemplate}"
DateFieldTemplate="{StaticResource TableRowDateFieldDataTemplate}"
DateTimeFieldTemplate="{StaticResource TableRowDateTimeFieldDataTemplate}"
DropdownFieldTemplate="{StaticResource TableRowDropdownFieldDataTemplate}"
FileFieldTemplate="{StaticResource TableRowFileFieldDataTemplate}"
GpsFieldTemplate="{StaticResource TableRowGpsFieldDataTemplate}"
GridFieldTemplate="{StaticResource TableRowGridFieldDataTemplate}"
ImageFieldTemplate="{StaticResource TableRowImageFieldDataTemplate}"
LabelFieldTemplate="{StaticResource TableRowLabelFieldDataTemplate}"
MultichoiceCheckboxFieldTemplate="{StaticResource TableRowMultichoiceCheckboxFieldDataTemplate}"
RadioFieldTemplate="{StaticResource TableRowRadioFieldDataTemplate}"
RangeSliderFieldTemplate="{StaticResource TableRowRangeSliderFieldDataTemplate}"
SignatureFieldTemplate="{StaticResource TableRowSignatureFieldDataTemplate}"
SplitterFieldTemplate="{StaticResource TableRowSplitterFieldDataTemplate}"
TextFieldTemplate="{StaticResource TableRowTextFieldDataTemplate}"
TextareaFieldTemplate="{StaticResource TableRowTextareaFieldDataTemplate}"
TimeFieldTemplate="{StaticResource TableRowTimeFieldDataTemplate}"
VideoFieldTemplate="{StaticResource TableRowVideoFieldDataTemplate}"/>
</UserControl.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Source={StaticResource FieldViewModelsSource}}"
ItemTemplateSelector="{StaticResource FieldViewModelDataTemplateSelector}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Margin="10,0,10,0" Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</UserControl>
Note that the DataTemplate resources in the UserControl resources are custom UserControls that are loaded in based on objects in the ViewModel (like in my original image 1).

So, with great thanks to #LovetoCode, I managed to remedy my issue. Still trying to get over my 2-day headache and bruises from banging my head against the desk, though.
I ditched the ScrollViewer and just used my custom UserControl:
<local:TableRowUserControl Grid.Row="1" Grid.ColumnSpan="2"
Margin="66,0,66,40" HorizontalContentAlignment="Stretch" />
Then, as #LovetoCode suggested - I used a ListView instead of ItemsControl. My most sincere apologies. I didn't want to use one first time round because...
Main issue was with the ListView's default style to have hover and tap effects; which I didn't need. I tried to steer clear of disabling the hover/tap from previous experience of failing - miserably.
After a bit of Googling (other search engines are available), I found a simple solution to do this quite easily.
I managed to do it like this:
<ListView x:Name="lstFieldViewModels" ItemsSource="{Binding Source={StaticResource FieldViewModelsSource}}" SelectionMode="None"
ItemTemplateSelector="{StaticResource FieldViewModelDataTemplateSelector}" IsSwipeEnabled="False">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Margin="10,0,10,0" Orientation="Vertical" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
Again, props to #LovetoCode. Gold star and programmer points for you :)

Related

Lock zIndex of a ContentPresenter Upon Mouse Enter or Drag Complete

I need to lock the Z-order of a canvas/content control after it is dragged by a Thumb.
In the below image, the "Joe Smith" pops above the others the other two ellipses while the the mouse over is active. Once the drag stops and the mouse moves out, it drops back to its value.
I am unable to find a solution within the design I have shown below to keep it locked above the others.
Minimal Reproducible Example
I have created a code gist of all code that contains the xaml, the thumb class and the people class. All one has to do is create a .Net Core WPF app and drop in the snippets of code and set name spacing in Xaml as needed.
Design
There is an ItemsControl which has DataTemplate defined with a Canvas. Each content in the ItemsControl has ContentControl which has a Thumb as applied by a style.
Another style trigger of the mouse entering the ContentPresenter has the content temporarily pushed in zIndex above the others by setting the content's internal Grid's zIndex to 1.
How to stick that zIndex?
Xaml
<ItemsControl Margin="10" ItemsSource="{Binding Source={StaticResource People}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas>
<ContentControl Width="100" Height="100" >
<Grid>
<Ellipse Fill="Silver">
<Ellipse.Effect>
<DropShadowEffect Color="Black" Direction="320" ShadowDepth="6" Opacity="0.5"/>
</Ellipse.Effect>
</Ellipse>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Margin="3,3,3,0" Text="{Binding Path=First}"/>
<TextBlock Margin="3,0,3,7" Text="{Binding Path=Last}"/>
</StackPanel>
</Grid>
</ContentControl>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style TargetType="{x:Type ContentPresenter}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Grid.ZIndex" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
See the Gist for all supporting classes and styles to reproduce
Actual Design
The ultimate goal is to have a panel of images of a set size, when the user grabs the thumb the image it will move forward and lock above the others. I say this in-case there is another way to do that which could provide an answer above the minimal example design.
In my ItemsControl I changed the ItemsPanelTemplate to be a Canvas such as
<ItemsPanelTemplate>
<Canvas x:Name="MainCanvas" />
</ItemsPanelTemplate>
Which when looking at the Visual Tree where the user was clicking the ContentControl, it had a parent of a Canvas that had a ContentPresenter with that top level Canvas such as (see named MainCanvas):
I changed the ContentControl to have a MouseEnter event :
<ContentControl Width="100" Height="100" MouseEnter="EnterMouse">
<Grid>
<Ellipse Fill="Silver">
In that method I needed to find the named "MainCanvas" Canvas and enumerate all of its children the ContentPresenters and extract the max ZIndex and then set my ContentControl (shown in blue above) to that ZIndex value plus 1.
Here is the code behind where extract the necessary the parents:
private void EnterMouse(object sender, MouseEventArgs e)
{
if (sender is ContentControl cc)
{
var cpParent = FindParent<ContentPresenter>(cc);
var p2 = FindParent<Canvas>(cpParent);
var max = p2.Children.Cast<UIElement>().Max(control => Panel.GetZIndex(control));
Panel.SetZIndex(cpParent, max + 1);
}
}
private T FindParent<T>(DependencyObject child) where T : DependencyObject
{
DependencyObject immediateParent = VisualTreeHelper.GetParent(child);
T parent = immediateParent as T;
return parent ?? FindParent<T>(immediateParent);
}

UserControl with Horizontal ListView in vertical ListView

I want to archive a vertical ListView that contains a UserControl in which i have some infos on the left and a second ListView that will scroll horizontal.
The problem is that my second listview will not start to scroll. It seems to pick unlimited space.
First of all some more infos about my setup:
My window is arranged via a grid basically just a row on top with text and the first listview in the second row.
The first listview uses a ItemTamplate like the following:
<ListView.ItemTemplate>
<DataTemplate>
<controls:MyControl />
</DataTemplate>
</ListView.ItemTemplate>
Beside that it has a ItemContainerStyle to display a horizontal line between the items:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem"
BasedOn="{StaticResource {x:Type ListViewItem}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<StackPanel>
<Rectangle x:Name="Separator"
Height="2"
SnapsToDevicePixels="True"
Fill="{DynamicResource AccentColorBrush}" />
<ContentPresenter />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
(trigger that disables the unnecessary first line omitted)
MyControl is a UserControl which is also just a grid first column some text and second column is my second listview.
This one is special because it is horizontal. Since their are various approaches for this from arround the internet i will show the one i ended using with.
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"
IsItemsHost="True"
IsVirtualizing="True"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
Item template is again a UserControl (at the moment just a TextBlock) and again the separator lines but this time vertically.
The problem is now that the second ListView is not scrollable. Even if i explicit set them to be visible they are disabled (also after resizing window).
My approach solving this was binding MaxWidth of Stackpanel in ListViewItem Template to Actual with of my MetroWindow.
MaxWidth="{Binding Path=ActualWidth, RelativeSource={RelativeSource AncestorType={x:Type metroControls:MetroWindow}}}"
But this and some other tries binding different sizes of different items does not work.
Can somebody give a hint to resolve this?

Too much spacing between StackPanel items in Windows Store app

I'd like to arrange some TextBlocks horizontally without any margins. I can see that my TextBlock is as small as it should be, but for some reason a lot of space is added around it. I think it has something to do with the ListView or its styling, but I don't know what.
I have a following layout:
<ListView Width="Auto" SelectionMode="None" Background="#654321" ItemsSource="{Binding}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Border Background="Black">
<TextBlock Text="{Binding}" Margin="0,0,0,0"/>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
<x:String>A</x:String>
<x:String>B</x:String>
<x:String>C</x:String>
<x:String>D</x:String>
<x:String>E</x:String>
</ListView>
What you need to change is the ItemContainerStyle. You can get the Style from here.
The big thing to notice is that the default Margin of the Style is:
<Setter Property="Margin" Value="0,0,18,2"/>
Which explains the large gap. Change it to:
<Setter Property="Margin" Value="0,0,0,2"/>
And that should help solve your issue.
One last thing to note is that they also have a default ContentMargin="4" (if using the ListViewItemsPresenter) or a bunch of Margins of 4 spread throughout the style (especially on the Borders), which you may need to change as well.
The other way to look at it - ListView is really designed as a control where you can tap, select, drag and otherwise interact with the items. I think if you want to remove all the features that ensure the sizes are touchable with fat fingers - you might just be better off using something like the base ItemsControl or ListBox which might not have these limitations. If you get rid of all the margins and leave your items so small - there's no point in using the ListView and it will just complicate things and make performance bad.

Setting Canvas Children Property without ItemsControl ItemsSource Binding Property

Is there any means to set Canvas Children Property without ItemsControl ItemsSource Binding Property?
In order to keep separated my view from viewmodel, I have to bind the items.
I have used the the canvas as a designer from 'CodeProject'
http://www.codeproject.com/KB/WPF/WPFDiagramDesigner_Part2.aspx
I'm using a canvas for drag-and-drop purposes. It works well when I work manually inside the canvas.
Which means I add and remove the child items using
myCanvas.Children.Add(userControl);
myCanvas.Children.Remove(userControl);
But if I load my usercontrols at run time, they are loaded just as views.
<s:Canvas AllowDrop="True" >
<ItemsControl Grid.Row="1" ItemsSource="{Binding Path=userControls}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<s:Canvas Background="Transparent"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<s:ControlItem Content="{Binding Path=MyView}"></s:ControlItem >
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding Path=X}" />
<Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</s:Canvas>
No, there aint. (Except manually clearing and adding...)
Ummm yeah just draw items inside the canvas? :)
<Canvas>
<TextBlock Text="I'm Child #1" />
<TextBlock Text="I'm Child #2" Canvas.Top="50" />
</Canvas>
Or you can always do it in code-behind
myCanvas.Children.Add(myTextBlock);
foreach(var someControl in SomeControlList)
myCanvas.Children.Add(someControl);
Edit
I see your update and have no idea what you're asking. If you want to drag/drop items onto a Canvas, you are better off adding/removing items from the ItemsSource than manually adding/removing items from the Canvas. Simply adding/removing them from myCanvas will not update the collection in your ItemsSource
I would recommend taking a look at Bea Stollnitz's article on dragging/dropping databound Items. This means you would keep the ItemsControl you have now, but when you drop an item on top of the Canvas it adds the DataItem behind that object to the ObservableCollection<MyDataItem> that you call userControls (I don't like this name because it suggests that the data items contain UI items, which should not be the case)

Hide control when another control overlaps it

I am having pretty big problem with windows forms controls hosted in WPF. When, for example, user scrolls the window, the hosted control goes on top of the window, although it should be hidden.
I know this is known problem, and default behavior of hosted controls, but I think it can be solved if control's visibility is somehow binded with: whether other controls overlap it, or not. If other controls are overlapping, it should become Collapsed or Hidden, if not, it should be Visible.
I made some kind of solution for this, but I did it on ScrollChanged event of a ScrollViewer and it works only in special situations. If somebody knows how to achieve that with binding, so it can be applied to any hosted control, please share your ideas.
For this same problem, we implemented something curious...
Windows forms host is unaffected by Z-order so scroll viewer wont be able to partially hide/ clip it for the area which is visible under the scrollviewer.
So we had two options...
Use Windows form host to host rest of the WPF UI in it which means we reverse the ownership of the UI. The WindowsFormsHost must host all the UI in it having a WinForms based scroll viewer which in turn will host the WPF UI.
Implement a scroll offset for calculated height of the windows forms host and when user scrolls add this offset to the scrollviewer's position and hide the windforms host yourself (Visibility = Hidden and NOT Collapsed). This way it gives an effect that you cannot partially scroll a winforms host but that scroll it completely off the scroll viewer. And because winformshost is Hidden (not collapsed) it continues to occupy that much height inside the invisible area under the scroll viewer (thereby maintaining its scroll position).
Let me know if this guides you in correct direction.
You can do a little trick. When you declare an WindowsFormsHost, it's parent is first HWND component. Usually it's root window. So, clip area for controls is whole window.
I'll show an example with WPF ScrollViewer.
<Window>
<Grid>
<ScrollViewer Margin="20,50">
<ItemsControl ItemsSource="{StaticResource StringArray}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<WindowsFormsHost>
<wf:Button />
</WindowsFormsHost>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Window>
In this case behaviour will be like you described. Buttons will be out of ScrollViewer bounds.
But there's a way to create "intermediate" HWND item to clip WinForms area over ScrollViewer. Just place another WindowsFormsHost with ElementHost like below:
<Grid>
<WindowsFormsHost Margin="20,50">
<ElementHost x:Name="This is clip container">
<ScrollViewer>
<ItemsControl ItemsSource="{StaticResource StringArray}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<WindowsFormsHost>
<wf:Button />
</WindowsFormsHost>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</ElementHost>
</WindowsFormsHost>
</Grid>
Now clip area for buttons is ElementHost and WinForms Buttons will be clipped by it on scrolling.
Also you can create ControlTemplate for ContentContol and reuse it where you need it.
<ControlTemplate x:Key="ClipConteiner" TargetType="{x:Type ContentControl}">
<WindowsFormsHost>
<ElementHost>
<ContentPresenter />
</ElementHost>
</WindowsFormsHost>
</ControlTemplate>
<Grid>
<ContentControl Template="{StaticResource ClipConteiner}" Margin="20,50">
<ScrollViewer>
<ItemsControl ItemsSource="{StaticResource StringArray}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<WindowsFormsHost>
<wf:Button />
</WindowsFormsHost>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</ContentControl>
</Grid>

Categories