Vertical scrolling in StackPanel without ScrollViewer - c#

I am trying to fix a larger block of code written by previous colleague - it i some sort of report system, output is a table with data. My task was to freeze column headerson top when scrolling. As i am new to this, I made very simple table, to find out how datagrid works:
public MainWindow()
{
this.InitializeComponent();
var dt = new DataTable();
dt.Columns.Add("prvni");
dt.Columns.Add("druhy");
for (int i = 0; i < 100; i++)
{
var row = dt.NewRow();
row[0] = "A" + i;
row[1] = "B" + i;
dt.Rows.Add(row);
}
this.MainGrid.ItemsSource = dt.AsDataView();
}
By lots of searching, I found many topics, which recommended to get rid of ScrollViewer, as the freezed headers are in datagrid by default. This was the original part of code I modified:
var scrollViewer = new ScrollViewer()
{
HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
VerticalScrollBarVisibility = ScrollBarVisibility.Auto
};
scrollViewer.AddHandler(UIElement.MouseWheelEvent, new RoutedEventHandler(this.MouseWheelHandler), true);
var stackPanel = new StackPanel();
scrollViewer.Content = stackPanel;
...
return scrollViewer;
And in another function, it was used/called as:
var reportInfo = ((((sender as DataGrid).Parent as StackPanel).Parent as ScrollViewer).Parent as ReportOutputTabItem).Tag as ReportInfo;
Well - I removed the scrollviewer, and was returning it as StackPanel, however - now I cannot scroll at all. When I searched questions, how to add vertical scrolling to StackPanel, answers were "add ScrollViewer".
So - is there a way, how either make column headers freezed inside the ScrollViewer, or how to enable vertical scrolling in StackPanel without using scrollViewer? (and another possible solution might be to make the vertical size of StackPanel bit shorter, as there are mostly pages of results, but full page is still required to scroll a bit).
XAML part:
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TabControl Name="MainTab" SelectionChanged="MainTabSelectionChanged" ItemTemplate="{StaticResource ClosableTabItemTemplate}"/>
<StackPanel Grid.Row="1" Name="NavigationPanel" Orientation="Horizontal" HorizontalAlignment="Center">
<Button Height="23" Name="FirstButton" Width="40" Content="<<" Click="PageButtonClick" Opacity="0.75"/>
<Button Height="23" Name="PrevButton" Width="40" Click="PageButtonClick" Opacity="0.75" Content="<"/>
<Label Height="23" Name="PageNumberLabel" Width="70" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Content="1/1"/>
<Button Height="23" Name="NextButton" Width="40" Content=">" Click="PageButtonClick" Opacity="0.75"/>
<Button Height="23" Name="LastButton" Width="40" Click="PageButtonClick" Opacity="0.75" Content=">>"/>
</StackPanel>
Thanks in advance.

Well, I finally found solution to this:
Originally, the datagrid was wrapped in the StackPanel, and then in ScrollViewer. I removed the ScrollViewer, and replaces StackPanel with Grid.
Now I have both vertical scrollbars, and frozen column headers.
I removed the entire
var scrollViewer = new ScrollViewer()
{
HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
VerticalScrollBarVisibility = ScrollBarVisibility.Auto
};
scrollViewer.AddHandler(UIElement.MouseWheelEvent, new RoutedEventHandler(this.MouseWheelHandler), true);
var stackPanel = new StackPanel();
scrollViewer.Content = stackPanel;
and replaced with simple var grid = new Grid();
and all stackPanel.Children.Add(dataGrid); replaced with grid.Children.Add(dataGrid);

Related

Dynamically added controls in stackpanel is not visible in wpf c#

I am dynamically adding textboxes based on a button click inside the stackpanel.But the textboxes are not visible in the UI .
Here is the code used for creating textboxs inside stackpanel.
public void GenerateControls()
{
TextBox txtNumber = new TextBox();
txtNumber.Name = "txtNumber";
txtNumber.Text = "1776";
txtNumber.Background= Brushes.Red;
panel1.Children.Add(txtNumber);
}
why its not visible..??and here is the XAML part of stackpanel
<StackPanel Name="panel1" Grid.Column="1" HorizontalAlignment="Left" Height="151" Margin="427,60,0,0" Grid.Row="2" VerticalAlignment="Top" Width="216">
<StackPanel Height="144">
</StackPanel>
</StackPanel>
If you are going to be adding controls dynamically, do not restrict the height (or even width) of the container you are adding to.
Update your XAML to have auto height/width.
<StackPanel Name="panel1"
Grid.Column="1"
Height="Auto"
Width="Auto"
Margin="427,60,0,0"
Grid.Row="2"
VerticalAlignment="Top"
HorizontalAlignment="Left" >
<StackPanel Height="144">
</StackPanel>
</StackPanel>
Also, once you add a new child, make sure you are updating the StackPanel layout.
public void GenerateControls()
{
TextBox txtNumber = new TextBox();
txtNumber.Name = "txtNumber";
txtNumber.Text = "1776";
txtNumber.Background= Brushes.Red;
panel1.Children.Add(txtNumber);
panel1.UpdateLayout();
}
In your xaml code, there is a stackpanel in your 'panel', it will be the 1st child of 'panel'.
And its height is 144px. your 'panel1' is 151 px.
So when you add textboxes into 'panel', they will be displayed behind the 144px stackpanel.
There is only 7px to display them. So they will not display on your window.

Datagrid is not enabling me to scroll between it's columns if I manually add the data

I'm creating a C# project where many columns of Combo Box are added programmatically in a datagrid.
Unfortunately when adding many columns, the datagrid is not able to display them all, and the horizontal scroll bar is disabled and unclickable..
Design :
<DataGrid ScrollViewer.CanContentScroll="True" ScrollViewer.HorizontalScrollBarVisibility="Visible" x:Name="dg" Grid.Column="1" Grid.Row="0" AutoGenerateColumns="false" Background="#FFDFF9F9" Height="76" VerticalAlignment="Top" Margin="2,82,0,0" HorizontalAlignment="Stretch"/>
Code :
foreach (var r in importMappings)
{
var dgtc = new DataGridTextColumn();
dgtc.Binding = new Binding(string.Format("[{0}]", r.Key));
var sp = new StackPanel();
dgtc.Header = sp;
sp.Children.Add(new Label { Content = r.Key });
var combo = new ComboBox();
sp.Children.Add(combo);
combo.ItemsSource = excelHeaders;
int x = combo.SelectedIndex;
var selectedBinding = new Binding(string.Format("[{0}]", r.Key));
selectedBinding.Source = importMappings;
combo.SetBinding(Selector.SelectedIndexProperty, selectedBinding);
dgtc.CanUserSort = false;
dgtc.CanUserReorder = false;
dg.Columns.Add(dgtc);
}
Live View :
The scroll bar is always disabled here.
Any ideas on how to make the scroll bar work ?
It looks like you don't have any data row. After adding a row of data the scrollbar worked for me.
For testing just edit your datagrid to:
<DataGrid ScrollViewer.CanContentScroll="True" ScrollViewer.HorizontalScrollBarVisibility="Visible" x:Name="dg" Grid.Column="1" Grid.Row="0" AutoGenerateColumns="false" Background="#FFDFF9F9" Height="76" VerticalAlignment="Top" Margin="2,82,0,0" HorizontalAlignment="Stretch">
<TextBlock />
</DataGrid>
The scrollbar should be working now.

Stackpanel "breaks" and has black background when the content is side is too much

I have the follow XAML:
<ContentControl HorizontalAlignment="Left" HorizontalContentAlignment="Left" Content="{Binding TotalReviewWordBlock}" Width="465" Margin="5,10,0,5" Foreground="#FF2D2D2D" Background="White"/>
and its binded to the following property:-
public StackPanel TotalReviewWordBlock
{
get
{
StackPanel st = new StackPanel();
st.Orientation = Orientation.Horizontal;
st.Background = new SolidColorBrush(Colors.White);
Paragraph pgf = new Paragraph();
Run r = new Run();
r.Text = App.Convert("Blah ");
r.FontWeight = FontWeights.Bold;
r.Foreground = new SolidColorBrush(CommonLib.rgbFromHexString("#FF2D2D2D"));
pgf.Inlines.Add(r);
int Rating = (int)(this.userrating * 2);
string ratingReplacement;
(some more code in the property itself...)
Run run = new Run();
run.Text = " " + this.myText;
run.Foreground = new SolidColorBrush(CommonLib.rgbFromHexString("#FF2D2D2D"));
pgf.Inlines.Add(run);
RichTextBox rtb = new RichTextBox();
rtb.TextWrapping = TextWrapping.Wrap;
rtb.Width = 450;
rtb.Blocks.Add(pgf);
st.Children.Add(rtb);
st.Background = new SolidColorBrush(Colors.White);
return st;
}
}
The problem is when the text is too much(say more that a 1000 character), or the height of the stackpanel is a lot, Its background becomes black. Its as if the stackpanel breaks) I noticed this earlier but at that time it was in a listbox and had multiple items to i simply made the width of each item 480, used blank grids instead of margins and it was "covered". But this time its just one big chunk of text(in a Paragraph). Let me know if you need ay other info. Please help!!
I worked around a similar "black stackpanel" problem by splitting the text into paragraphs to form a List<String>. And then that list of strings would be the ItemsSource of a ListBox.
So instead of a very large StackPanel, I ended up with a long ListBox.
I also prevented user interaction in the ListBox and vertical scroll by using IsHitTestVisible="False" and ScrollViewer.VerticalScrollBarVisibility="Disabled"
So, the ListBoxended up as follows:
<ListBox x:Name="listBox" IsHitTestVisible="False" ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Background="White">
<TextBlock TextWrapping="Wrap" Text="{Binding}"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And in code behind:
textSplitInParagraphs = new List<String>();
// add paragraphs to the list...
listBox.ItemsSource = textSplitInParagraphs;
Don't know if it is the correct workaround, but I helped me, after some time of banging my head against the table.
Hope this helps.

Adding Grid/FrameworkElement to ContainerVisual

Well, this might be a strange question. But as title says, can you add Grid to ContainerVisual. Since Grid inherits Visual, I should be able to do it via Children.Add.
Why do I need this? Well, I'm using FlowDocument to print a report. This report needs to have a header, and since Flow Document doesn't support Headers, I decided to add headers during pagination, by using a solution found on the internet.
Also since I don't want to draw entire header by hand but be able to edit it during desing in a designer, I designed it in a separate file as a Grid element.
So my header looks something (I shortened it) like this:
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="2cm" Width="18.7cm">
<Grid.Resources>
<!-- some resources -->
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2cm"/>
<ColumnDefinition/>
<ColumnDefinition Width="2cm"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Grid.RowSpan="2">
<Image Source="Logo.jpg"/>
</Border>
<Border Grid.Column="1" Grid.RowSpan="2">
<StackPanel>
<Label Name="staje" Style="{DynamicResource naslov}"></Label>
<Label Name="predmet" Style="{DynamicResource naslov}"></Label>
</StackPanel>
</Border>
<Border Grid.Column="2" BorderThickness="1">
<StackPanel>
<TextBlock>Datum:</TextBlock>
<Label Name="datum">22. 12. 2013.</Label>
</StackPanel>
</Border>
<Border Grid.Column="2" Grid.Row="1" BorderThickness="1,0,1,1">
<StackPanel>
<TextBlock>Strana:</TextBlock>
<Label Name="strana">
1/2
</Label>
</StackPanel>
</Border>
</Grid>
at each pagination call I load the header using the folowing code:
public FrameworkElement GetHeaderForPage(int Strana)
{
FrameworkElement header = Application.LoadComponent(new Uri("/Header.xaml", UriKind.Relative)) as FrameworkElement;
Label lblTest = LogicalTreeHelper.FindLogicalNode(header, "staje") as Label;
Label lblPredmet = LogicalTreeHelper.FindLogicalNode(header, "predmet") as Label;
Label lblDatum = LogicalTreeHelper.FindLogicalNode(header, "datum") as Label;
Label lblStrana = LogicalTreeHelper.FindLogicalNode(header, "strana") as Label;
lblTest.Content = KakavTest;
lblPredmet.Content = Predmet;
lblDatum.Content = Datum;
lblStrana.Content = string.Format("{0}", Strana);
return header;
}
And finally in the pagination call I place it in the page like so:
DocumentPage page = m_Paginator.GetPage(pageNumber);
// Create a wrapper visual for transformation and add extras
ContainerVisual newpage = new ContainerVisual();
FrameworkElement header = headerGen.GetHeaderForPage(pageNumber);
// header.RenderTransform = new TranslateTransform(0, -header.ActualHeight+10);
ContainerVisual smallerPage = new ContainerVisual();
smallerPage.Children.Add(page.Visual);
//smallerPage.Transform = new MatrixTransform(0.95, 0, 0, 0.95,
// 0.025 * page.ContentBox.Width, 0.025 * page.ContentBox.Height);
newpage.Children.Add(smallerPage);
newpage.Children.Add(header);
newpage.Transform = new TranslateTransform(m_Margin.Left, m_Margin.Top);
RenderTargetBitmap bmp = new RenderTargetBitmap((int)m_PageSize.Width, (int)m_PageSize.Height, 96, 96, PixelFormats.Default);
bmp.Render(newpage);
ImageShow show = new ImageShow(bmp);
show.Show();
return new DocumentPage(newpage, m_PageSize, Move(page.BleedBox), Move(page.ContentBox));
ImageShow class simply opens up new window with an image representing bmp. I was using it to see if the problem was in further processing that is done to display the pages in DocumentViewer. But since ImageShow doesn't display the header Grid, it seems tht I'm doing something terribly wrong.
IN SHORT:
Can you add Grid element to ContainerVisual as a child and have it be drawn correctly. Or do I need to draw it by hand?
In the end I hard coded it all by hand. So no, anything that is higher than VIsual and DrawingVisual, can not be included into ContainerVisual.

stackpanel in datapager silverlight

I am developing a silverlight navigation application and got stuck on the following problem...
The guy I am developing the app wants to have a News page where you can see all published news on the left side and the clicked (or latest news if none is clicked) on the right side. He wanted to have a header, text and publishing date for each news in the news list. Also he wanted to have paging so that there won't be to many news in the list at once...
I did this:
foreach (Model.News news in s)
{
StackPanel stackPanel = new StackPanel();
HyperlinkButton hyperlinkButton = new HyperlinkButton();
hyperlinkButton.Tag = news.Header;
hyperlinkButton.Content = news.Header;
hyperlinkButton.FontSize = 15;
hyperlinkButton.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
hyperlinkButton.Click += new RoutedEventHandler(Button_Click);
stackPanel.Children.Add(hyperlinkButton);
TextBlock textBlock = new TextBlock();
textBlock.Foreground = new SolidColorBrush(Colors.Gray);
textBlock.FontSize = 12;
textBlock.FontFamily = new FontFamily("Verdana");
textBlock.TextWrapping = TextWrapping.Wrap;
textBlock.Text = news.Text;
stackPanel.Children.Add(textBlock);
TextBlock dateTextBlock = new TextBlock();
dateTextBlock.Foreground = new SolidColorBrush(Colors.Gray);
dateTextBlock.FontSize = 10;
dateTextBlock.FontFamily = new FontFamily("Verdana");
dateTextBlock.TextWrapping = TextWrapping.Wrap;
dateTextBlock.FontWeight = FontWeights.Bold;
dateTextBlock.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
dateTextBlock.Text = news.Date.ToShortDateString();
stackPanel.Children.Add(dateTextBlock);
stackPanel.Children.Add(new TextBlock());
newsStackPanel.Children.Add(stackPanel);
}
PagedCollectionView itemListView = new PagedCollectionView(newsStackPanel.Children);
newsPager.Source = itemListView;
and all of it goes here
<Grid x:Name="LayoutRoot" Loaded="LayoutRoot_Loaded" MaxWidth="1100">
<Grid.RenderTransform>
<CompositeTransform/>
</Grid.RenderTransform>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="2"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<RichTextBox Name="contentRTB" MaxWidth="1000" Margin="10, 30, 10, 30" Grid.Column="2"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
TextWrapping="Wrap"
Style="{StaticResource RichTextBoxStyle}" IsReadOnly="True"/>
<Rectangle Grid.Column="1" Margin="0,10"
Fill="#FF0067C6"/>
<TextBlock Name="header" Foreground="#FF0067C6" FontSize="18" FontFamily="Verdana" HorizontalAlignment="Center" VerticalAlignment="Top" Grid.Column="0"></TextBlock>
<sdk:DataPager x:Name="newsPager"
DisplayMode="FirstLastNumeric"
Background="#FF0067C6"
PageSize="3"
AutoEllipsis="True"
NumericButtonCount="3"/>
<StackPanel Name="newsStackPanel" Grid.Column="0" Orientation="Vertical" Margin="0,50,0,0"/>
</Grid>
The newsPager displayes (correctly) 2 pages because i have currently 5 news and the pageSize is set to 3 but they are all displayed on the same page so I dont get the desired paging... how can i fix it
Your code is adding all the items to a StackPanel, then it is putting that StackPanel inside another StackPanel called "newsStackPanel" below the DataPager. So, right now, the DataPager has nothing to do with the display of your news articles, and you won't be seeing any paging happening.
Instead, take a look at the DataPager sample code here:
http://msdn.microsoft.com/en-us/library/system.windows.controls.datapager(VS.95).aspx#Y9406
You'll need to modify that sample code to contain a list of StackPanels like this:
List<StackPanel> itemList = new List<StackPanel>();
Then, for each of your news items, add them to that list instead of an outer StackPanel:
itemList.Add(stackPanel);
You'll then wrap that up and bind it to both your data pager and a new list control:
// Wrap the itemList in a PagedCollectionView for paging functionality
PagedCollectionView itemListView = new PagedCollectionView(itemList);
// Set the DataPager and ListBox to the same data source.
newsPager.Source = itemListView;
listBox1.ItemsSource = itemListView;
The sample uses a ListBox called "listBox1". You have lots of choices there. Perhaps replace the "newsStackPanel" with a ListBox called "newsList".
OK, that should be enough to get you through this.
Now for a little more homework:
You should really consider switching this to the MVVM pattern where you bind these values and template them instead of making UI controls in C#. This results in much cleaner code, enables much easier reuse, improves testability, and so on. There are a million zillion articles on the web for this. Here is one from MS:
http://msdn.microsoft.com/en-us/library/gg430869(v=PandP.40).aspx
I don't know if the DataPager control you're using handles the paging completely.
You could only add the news items that are on the page you want to view to the stack panel.
One easy way to do that could be to use LINQ in your for each, something like:
foreach (Model.News news in s.Skip(newsPager.PageSize * newsPager.PageIndex).Take(newsPager.PageSize))
you'd have to reinitialize the items in the pager when the page index changes then too.

Categories

Resources