I am using a TabControl to programmatically show or hide groups of form controls. I have implemented the technique described here and it approximately works as expected, except that there is a band approximately 1 or 2 pixels high in the location where the tab headers are normally displayed.
I have verified this by using Snoop to navigate the visual tree and observe the movement of the highlight rectangle as each element is selected. The size of the rectangle for the tab content element is fractionally smaller than that of the containing TabControl, which accounts for the extra pixels I am seeing. None of the elements that might affect this have margin, border or padding.
To achieve proper alignment with other controls, I need to eliminate this extra space, but I am not sure how. However, perhaps the question I should be asking is "is there a better way to selectively show / hide groups of controls?".
Thanks for your ideas,
Tim
I suppose the thin line is caused by the TabPanel which is still there even though all TabItems are collapsed.
However, you could change the TabControl's ControlTemplate and bind the TabPanel's Visibility to the number of tabs, like this:
<TabPanel ... Visibility="{Binding Items.Count, RelativeSource={RelativeSource FindAncestor, Type={x:Type TabControl}}, Converter={StaticResource ZeroToCollapsedConverter}}" ... />
Of course, you will have to implement a converter which converts 0 to Visibility.Collapsed and all other values to Visibility.Visible.
BTW: You can get the default ControlTemplate for the TabControl here.
Related
I got an interesting issue inside my wpf application.
I'm working with mvvm, and items are composed within a content control that bound to the view model.
For example:
<Shell>
<ScrollViewer>
<StackPanel>
<ContentControl Content="{Bidning SomeVM}"/>
<ContentControl Content="{Bidning SomeVM2}"/>
<ContentControl Content="{Bidning SomeVM3}"/>
</StackPanel>
</ScrollViewer>
</Shell>
If the stack panel got only one item it works fine, but for some reason each view (resolved form vm) require much more space(height) than it actually need.
The views contains a GroupBox and a datagrid inside if it's matter.
So this is my problem, any suggestions?
A StackPanel is going to measure it's children with Infinity in the direction of the Orientation, which is Vertical by default. So your items will be measured with an infinite height which essentially means they're going to size to content - e.g. the DataGrid will allocate, measure and arrange a record for every item in the list (assuming no constraints were set on the control). Note too that their will be no virtualization as a result since that relies on being measured with a specific constraint.
Note, even if you used a different panel you might get this behavior as the ScrollContentPresenter within the ScrollViewer will measure it's children with infinity if it is performing the scrolling - which will happen if the CanContentScroll is set to false or if the ItemsPanel doesn't implement IScrollInfo.
I'm not sure what type of layout you're looking to acheive so it's hard to offer any specific recommendations.
Ok, so I have a DataGrid (the standard WPF DataGrid, which comes with .Net 4.0, NOT the WPF Toolkit DataGrid) with the CanUserAddRows=false. It's bound to an ObservableCollection in the ViewModel. It has a MaxHeight set properly, so that it will scroll if there are too many rows for the screen.
It works just fine the way it is, except for the fact that if the user puts their mouse over the DataGrid and then moves the Scroll-wheel downwards, then some extra space appears below the rows:
I would rather that the gray space does not appear in this case (where there is no need to scroll). How do I accomplish this?
P.S. I've actually built my own functionality for a new row at the bottom of the DataGrid due to some special requirements for our program, thus the blank row at the bottom of the DataGrid. It's done totally in the VM, so it shouldn't affect the answer to this question.
Update:
This behavior happens on every DataGrid I have currently. However, when the MaxHeight is set on the DataGrid, and there are more rows than can be displayed, then the content starts to scroll. In this situation, the grey space below the lines is variable in size. That is, since the DataGrid scrolls based on content rather than physical scrolling (see this for details about the difference, under the remarks section), there is a little extra space at the bottom below the last row when you scroll all the way to the bottom. The grey space fills that extra space. Here is an example:
To clarify, I don't mind that behavior that much, it's just when the grey space appears when there is no need for scrolling. I just thought that this behavior would help indicate the cause of the problem.
Update #2:
I have discovered what can cause the problem: if you set the EnableRowVirtualization to false, then this problem occurs. However, if I want to set it to false, how can I prevent the grey space/"extra line" from occurring when there is no need to scroll? (this is my main concern and the main point of this question)
Try this: Set
ScrollViewer.CanContentScroll to False
ScrollViewer.HorizontalScrollBarVisibility to Disabled
Then
<DataGrid x:Name="myDataGrid"
RowHeaderWidth="{Binding RelativeSource={RelativeSource Self}, Path=RowHeight}"
ScrollViewer.CanContentScroll="False"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Width="*" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Age" Width="*" Binding="{Binding Age}"/>
</DataGrid.Columns>
</DataGrid>
I've faced the same issue. In my case extra space at the bottom was because MaxHeight being set and bacause of
VirtualizingPanel.ScrollUnit="Item"
After setting it to
VirtualizingPanel.ScrollUnit="Pixel"
the problem actually dissapears (still having MaxHeight property)
You question is a little unclear but I can offer some investigative steps to find out what the cause is.
Make sure that your observable collection is not harboring any null values. A null entry in the collection will still show up in the grid as a blank row.
The fact that it is happening on different environment means that your data source may be different, so that is a good place to check. Check here that the count of the collection is directly matched with the number of rows on the grid (including the empty row).
Make sure that this is not a control templating issue. If possible, look at the Style and DataTemplate for your DataGrid control to see if this is not a visual side-effect.
Apart from that, you should include more details in your question, such as the type of DataGrid you are using (eg. WPF Toolkit, Telerik, DevExpress, etc) so that we can rule out a templating issue.
I had the same problem and it was caused by the DataGrid's MaxHeight property not being set to a multiple of the row size, so the left over space was showing up at the bottom of the DataGrid. For example: if there are 5 rows that are 10 pixels in height and the MaxHeight property is set to 55 then there will be 5 pixels of space at the bottom.
Somehow I guess it may be because that DataGrid has to show the first visible row at row boundary, e.g. it can't display only the lower half of the first visible row in order to make the last row align with the bottom of the control.
You can test that by two steps:
1) scroll to the bottom of your DataGrid, and see the extra gray space;
2) slowly resize the DataGrid (or its containing window) vertically, and see how that gray space change, while noting the first visible row stays unmoved.
I had a similar problem and the answer is simple.
All you have to do is to put the following in your datagrid definition in XAML:
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding ItemList}"
CanUserAddRows="false"
and you are clear.
Another similar stuff is, if you get a blank column in your data grid then the solution is to put the Width="*" in your last column definition also in XAML.
Hope that works for you.
Set ScrollViewer.CanContentScroll="False" & ScrollViewer.HorizontalScrollBarVisibility="Disabled" in DataGrid.
set both ScrollViewer.CanContentScroll and CanUserAddRows to false.
I'm binding a list of object to an ItemsControl which contain a TextBox control.
I want to be able to change the textbox background color based on the background color property of the bind object.
It works perfectly with the INotifyPropertyChanged interface but when I need to update let say 1000 objects it takes a huge amount of time I guess because it needs to update the controls one by one.
Does someone has a tip for me to lets say, update all my objects background color and than update the binding in one shot instead of object by object?
Thanks,
Mat
If you use virtualization, it'll only need to update the items that are visible - normally items controls will only track property changes on the items that are currently visible. However, if you're using the base ItemsControl, virtualization will be off by default. To turn it on, you need to provide a custom template that includes a ScrollViewer with CanContentScroll set to True, and you also need to use a VirtualizingStackPanel, either in the control template, or via the ItemsPanel - this shows the former approach:
<ItemsControl ItemsSource="...whatever...">
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<ScrollViewer CanContentScroll="True">
<VirtualizingStackPanel IsItemsHost="True" />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
Virtualization improves performance with large numbers of list items in various ways. It should help your scenario, but it may also improve other aspects of the performance. It's not without its problems, but I'd definitely try it here first before attempting other solutions.
I finally found why it was taking me so long to update my UI. Byt the way, What I'm trying to achieve is a kind of spreadsheet grid. My cell container was a border with the borders visible. I was updating the Border control Background color based on cell selection. Disabling the borders of the Border control resolved my issue right away. The UI update time went from 4-5 seconds to instantaneous.
Thank you guys for your answer, I will still try to implement your suggestion to improve even more my project.
Thanks
Mat
FindAncestor RelativeSource only supports 'Self' and 'TemplatedParent',
but I have to bind the width of a popup to the width of the page.
Giving the page a name causes problems because sometimes it will
throw exceptions saying a control with that name is already present in the visual tree.
<Popup IsOpen="True"
Width="{Binding ElementName=BordPage, Path=Width}"
Height="{Binding ElementName=BordPage, Path=Height}">
Background information:
I'm using a SL4 navigation based application here. BordPage is a navigation page,
which I'm using multiple times within the application. So giving it a name in the page itself is not really a good idea,
but I don't know how else I can bind to the width and height of the page.
What I'm trying to do is have a black border (with opacity 0.8) cover the entire screen,
(including the controls of the MainPage). Then on top of that I want to display some other controls.
Since the application is touch controlled, providing the user with a ComboBox to select a value doesn't really work wel. Instead I want to show this black overlay window with a listbox taking up most of the screen so the user can simply touch the value he wants with a single click.
Update: I just realized I can use the ChildWindow class to do this.
But my original question remains.
My general solution for this problem is by writing a custom behavior. It's not a pure XAML solution but it gives you a lot more flexibility.
Create a behavior that searches up the VisualTree to find the right item and then have it set the width of the Popup correctly.
It may be a little more complicated than a straight binding but it avoids all the naming issues.
Put the following in the constructor of your control so you can avoid naming it:
DataContext = this;
I have some controls added in a stackpanel programmatically. What i want to do is that i want one of the controls in this stackpanel to be placed over another control. Specifically, I want to place button over an image in this stack panel. I couldn't find zindex property in c# codebehind. Although it seems very simple problem but i am unable to find any clue to solve this problem. Anyone please......??
Try placing all your controls on Canvas. Then you can set Zindex with:
this.controlName.SetValue(Canvas.ZIndexProperty, 10d);
Only the Canvas panel supports a ZIndex property. Stackpanel doesn't because each item is placed one after the other in the panel so they shouldn't overlap each other. This can be a little annoying at times when you have animated transforms moving the items about because the previous assumption isn't actually true.
In general though if you need to place items in a visual stack the Stackpanel isn't the right place for it. Perhaps a Canvas or you could use a Grid where the oridinal position of a element determines its "zorder" in a cell.
From xaml:
<StackPanel Canvas.ZIndex="1">
</StackPanel>