Good day! I want to make a scrollable image grid like "Unsplash" site, but with WPF. I using Grid and ListBoxes, but the Grid not scrolling, scrolling only 3 ListBoxes apart from each other. What I should use for that type of thing?
What I need
What I have with wrong scrolling
WPF code:
<Grid ScrollViewer.VerticalScrollBarVisibility="Visible">
<Grid.ColumnDefinitions>
<ColumnDefinition>
</ColumnDefinition>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0" x:Name="listBox0" ScrollViewer.VerticalScrollBarVisibility="Disabled">
</ListBox>
<ListBox Grid.Column="1" x:Name="listBox1" ScrollViewer.VerticalScrollBarVisibility="Disabled">
</ListBox>
<ListBox Grid.Column="2" x:Name="listBox2" ScrollViewer.VerticalScrollBarVisibility="Disabled">
</ListBox>
</Grid>
List filling:
for (int i = 0; i < 20; i++)
{
Rectangle temp = new Rectangle();
temp.Width = 250;
temp.Height = rnd.Next(100, 300);
temp.Fill = GetRandColor();
if(listStateCounter == 2)
{
listStateCounter = 0;
listBox2.Items.Add(temp);
}
else if (listStateCounter == 1)
{
listStateCounter++;
listBox1.Items.Add(temp);
}
else if (listStateCounter == 0)
{
listStateCounter++;
listBox0.Items.Add(temp);
}
}
try this
<ScrollViewer VerticalScrollBarVisibility="Visible">
<Grid>
//.............................................
</Grid>
</ScrollViewer>
Related
I need to create a scrollviewer with X amount of items, i made the code that create a border with an image and sets into a grid, thats the code:
public partial class MainWindow : Window
{
panel panels = new panel();
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
int col = -1;
int actual_row =0;
for (int i = 0; i != 20;i++)
{
col++;
Grid g = panels.create_panel();
scroll.Children.Add(g);
g.SetValue(Grid.RowProperty, actual_row);
g.SetValue(Grid.ColumnProperty, col);
if (col == 3)
{
actual_row++;
col = -1;
//aad row
RowDefinition newrow = new RowDefinition();
scroll.RowDefinitions.Add(newrow);
}
}
}
}
and her is the XAML:
<Window x:Class="pretyListbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:pretyListbox"
mc:Ignorable="d"
Background="#282828"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<Style x:Key="item" TargetType="Grid">
</Style>
</Window.Resources>
<Grid>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Standard"
>
<Grid x:Name="scroll">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
</Grid>
</ScrollViewer>
<Button Height="100" Width="100" Click="Button_Click" Content="gen" Margin="0,0,3,1" HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
</Grid>
</Window>
I put an image of the app working:
App working
And there is the lag:Lagged app
the lag only appears the first time it get resized or scrolled, but isnt about time bc i wait 1 min and it get lagg equally.
If someone can tell me how to repair that or charge the grid after with an async methot i will be very greatefull. Thnx.
Use ItemControl and Bind all items to ItemSource, It will create Vertical scroll bar if required
Please look at below example
<ItemsControl x:Name="CountryCodeFlagControl"
ItemsSource="{Binding SourceList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate>
<Border>
<ScrollViewer>
<ItemsPresenter />
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Value}">
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
i resolve my answer, i just put an asynchronous methot and now it works perfectly every time.
The code :
private async void Button_Click(object sender, RoutedEventArgs e)
{
int col = -1;
int actual_row =0;
for (int i = 0; i != 100; i++)
{
Task task = Task.Factory.StartNew(() =>
{
col++;
Dispatcher.Invoke(() =>
{
Grid g = panels.create_panel();
scroll.Children.Add(g);
g.SetValue(Grid.RowProperty, actual_row);
g.SetValue(Grid.ColumnProperty, col);
if (col == 3)
{
actual_row++;
col = -1;
//aad row
RowDefinition newrow = new RowDefinition();
scroll.RowDefinitions.Add(newrow);
}
});
});
await Task.Delay(100);
await task;
}
}
i populated some text ( i name them as questions) from database but in my wrap panel i can only display 3 questions , the rest of the questions are being cut-off , i have more than 3 questions , how can i do a paging in wrap panel to display the rest of the question on the panel itself? i populate the questions by for loop like this:
for( int i= 0 ; i<lstQuestion.Count()-2; i++)
{
TextBlock tb = new TextBlock(); // Question
tb.FontSize = 19;
tb.FontWeight = FontWeights.Bold;
tb.Text = lstQuestion[i].QuestionContent;
tb.TextWrapping = TextWrapping.Wrap;
wrapPanel1.Children.Add(tb);
TextBox tbox = new TextBox();
if (lstQuestion[i].Answer.Trim().Length > 0) // Textbox for user to input answer in every question
{
tbox.FontSize = 19;
tbox.Width = 250;
tbox.Height = 50;
tbox.PreviewDrop += new DragEventHandler(tbox_PreviewDrop);
tbox.Focusable = false; // Disallow user to input anything into it.
wrapPanel1.Children.Add(tbox);
}
answers.Add(lstQuestion[i].Answer);
if (lstQuestion[i].QuestionNo != lstQuestion[i + 1].QuestionNo) // add spacing between question
{
StackPanel sp = new StackPanel();
sp.Width = 1010;
wrapPanel1.Children.Add(sp);
Label spacing = new Label();
spacing.Width = 1010;
spacing.Content = "";
wrapPanel1.Children.Add(spacing);
}
} // end of for each loop.
And in my xaml :
<Grid>
<WrapPanel HorizontalAlignment="Center" Name="wrapPanel1" VerticalAlignment="Center" Height="400" Width="1038" Margin="0,77,0,43" />
<WrapPanel Height="80" HorizontalAlignment="Center" Name="wrapPanel2" VerticalAlignment="Top" Background="Aquamarine" Width="1000">
</WrapPanel>
<Button Content="Check" Height="37" HorizontalAlignment="Center" Name="button1" VerticalAlignment="Bottom" Width="90" FontSize="24" Margin="474,0" Click="button1_Click" />
</Grid>
Wrappanel1 is where i put my questions and textbox ( wrap panel 1 is the panel that i want to do paging in ) , wrappanel2 is another panel where choices of answers are there.
As you can see from the grey arrow , there is still more text , but it just stops there at the end of the scroll.
Put your wrapPanel1 inside a ScrollViewer.
<ScrollViewer VerticalAlignment="Center" HorizontalAlignment="Center" Height="400" Width="1038">
<WrapPanel Name="wrapPanel1" />
</ScrollViewer>
Try arranging your UI in XAML with RowDefinitions
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="80"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
</Grid.RowDefinitions>
<WrapPanel Grid.Row="0" HorizontalAlignment="Center" Name="wrapPanel2" Background="Aquamarine" Width="1000"></WrapPanel>
<ScrollViewer Grid.Row="1" Height="400" Width="1038" CanContentScroll="True" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<WrapPanel Name="wrapPanel1"/>
</ScrollViewer>
<Button Grid.Row="2" Content="Check" Margin="5" HorizontalAlignment="Center" Name="button1" VerticalAlignment="Bottom" Width="90" FontSize="24" Click="button1_Click" />
</Grid>
I am using XAML and MVVM pattern for an application.
In part of my application I have a window split into 2 parts, a left column and right hand column/area
In the left column are a row of buttons ( generated on loading the window )
The right column/area will display a group of buttons depending on which button the user clicks in the left column, so for example say I have 4 department buttons in the left column, each department button will have a different number of stock items.
If I click button 1 in the left column, I do some code in the viewmodel to fetch the names of items in that department. And then I build a new observableCollection to display in the right column/area.
So this isnt a problem, the right amount of buttons get generated each time ok.
However when I try to change the background colour dynamically in the ViewModel, the colour is NOT upodated in the view.
The strange thing is, I can change the content, forecolour and other properties but just not the background colour. It appears the background will only generate correctly if loaded and run time. I cant change it otherwise while using the window.
I have tried Brushes, creating and assigning new style and even clearing the dependancy property of the button ( .ClearValue(Button.BackgroundProperty) )
Would any one know how i can get the background to change colour while the window is open and when i want to generate a set of buttons dynamically in my viewmodel?
Many thanks all... I have attached my XAML and C# snippet, the
XAML :
<dxd:DockLayoutManager Name="dlSalesScreen">
<dxd:DockLayoutManager.LayoutRoot>
<dxd:LayoutGroup Name="Root" Orientation="Horizontal" AllowSplitters="False">
<dxd:LayoutPanel AllowClose="False" AllowRename="False"
Caption="Departments" HorizontalScrollBarVisibility="Hidden"
CaptionAlignMode="AutoSize"
CaptionImageLocation="BeforeText" ShowPinButton="False" >
<!-- Scrollviewer for department buttons-->
<ScrollViewer x:Name="deptScrollviewer" Grid.Row="0" Grid.Column="0" Width="185" Height="Auto" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" PanningMode="VerticalOnly">
<ItemsControl ItemsSource="{Binding Departments}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel VerticalAlignment="Top" Height="Auto" Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
</dxd:LayoutPanel>
<dxd:LayoutPanel AllowClose="False" AllowRename="False"
Caption="Available stock in department" Width="Auto"
CaptionAlignMode="AutoSize"
CaptionImageLocation="BeforeText" ShowPinButton="False">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<!-- Scrollviewer for stock buttons-->
<ScrollViewer x:Name="stockScrollViewer" Grid.Row="0" Width="Auto" Height="Auto" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" PanningMode="VerticalOnly">
<ItemsControl ItemsSource="{Binding StockItems, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel VerticalAlignment="Top" HorizontalAlignment="Left" Orientation="Horizontal" Width="400" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
<Rectangle Grid.Row="1" Margin="0,0,0,0" Height="Auto" Fill="{StaticResource BottomRectangleGradient}" />
<Grid Name="gridButtonHolder" Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<GroupBox x:Name="grpStockItem" Grid.Column="0" Header="Selected Item" HorizontalAlignment="Left" Width="200" >
<Label x:Name="lblStockName" Content="{Binding SelectedStockItemLabel}" HorizontalAlignment="Left" />
</GroupBox>
<Button Name="btnSave" Content="Apply" Command="{Binding ConfirmSelectionCommand}" dxc:ThemeManager.ThemeName="Office2007Blue" Grid.Column="1" Width="110" Height="42" Margin="0,8,0,0" />
<Button Name="btnClose" Content="Cancel" dxc:ThemeManager.ThemeName="Office2007Blue" Grid.Column="2" Width="110" Height="42" Margin="0,8,0,0" />
</Grid>
</Grid>
</dxd:LayoutPanel>
</dxd:LayoutGroup>
</dxd:DockLayoutManager.LayoutRoot>
</dxd:DockLayoutManager>
C#
void DeptClicked(object sender, RoutedEventArgs e)
{
SelectedDeptID = Convert.ToInt32(((Button)sender).Tag.ToString());
_stockButtons = new ObservableCollection<Button>();
if (StockItemCount > 0)
{
for (int i = 0; i < StockItemCount; i++)
{
//_stockButtons.Add(new Button());
Button btn = new Button();
btn.Background = Brushes.Aquamarine;
btn.Height = 100;
btn.Width = 100;
btn.Content = i.ToString();
_stockButtons.Add(btn);
}
}
RaisePropertyChanged("StockItems");
}
public ObservableCollection<Button> Departments
{
get
{
if (_deptButtons == null)
{
_deptButtons = new ObservableCollection<Button>();
for (int i = 0; i < DeptCount; i++)
{
Button button = new Button();
button.Content = DepartmentNames[i];
button.Tag = DepartmentIDs[i].ToString();
button.Click += new RoutedEventHandler(DeptClicked);
button.Width = 128;
button.Height = 100;
_deptButtons.Add(button);
}
}
return _deptButtons;
}
}
Try something like that:
Button btn = new Button();
btn.Background = Brushes.Green;
btn.Height = 100; btn.Width = 100;
btn.Content = i.ToString();
ThemeManager.SetThemeName(btn, "None");
_stockButtons.Add(btn);
Class ThemeMagager is in namespace DevExpress.Xpf.Core.
I have a grid that I need to put into a border, doing this via XAML is easy
but how do I do this via C#?
everything that I have found thus far wants to add the border around each cell.
I need it to come out looking the same way XAML does it, please help!
I can not get the XAML to post correctly here:
<Border Grid.Column="1"
Grid.Row="0"
Background="AliceBlue"
BorderBrush="Black"
BorderThickness="4"
x:Name="Side6"
Visibility="Collapsed">
<UIElement.Projection>
<PlaneProjection RotationY="-90" />
</UIElement.Projection>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100"></RowDefinition>
<RowDefinition Height="100"></RowDefinition>
<RowDefinition Height="100"></RowDefinition>
</Grid.RowDefinitions>
<Button Grid.Column="2" Grid.Row="1" Click="RotateRight_Click">
<Button.Content>
<StackPanel>
<TextBlock HorizontalAlignment="Center">Rotate Right</TextBlock>
<TextBlock HorizontalAlignment="Center">To</TextBlock>
<TextBlock HorizontalAlignment="Center">Side 4</TextBlock>
</StackPanel>
</Button.Content>
</Button>
<Button Grid.Column="0" Grid.Row="1" Click="RotateLeft_Click">
<Button.Content>
<StackPanel>
<TextBlock HorizontalAlignment="Center">Rotate Left</TextBlock>
<TextBlock HorizontalAlignment="Center">To</TextBlock>
<TextBlock HorizontalAlignment="Center">Side 2</TextBlock>
</StackPanel>
</Button.Content>
</Button>
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Grid.Column="1"
Grid.Row="1"
Text="Side 6">
</TextBlock>
</Grid>
</Border>
Here is the C# code that I'm using, maybe you can see what I am doing wrong?
public static void panelMain(string strPassGridName, System.Windows.Media.Color mcPassColor,
int intRowProperty, int intColumnProperty, Visibility vVisibility,
string[] strButtonTitles, Grid passLayoutRoot, Canvas passCanvas)
{
Grid panelGrid = new Grid();
panelGrid.Name = strPassGridName;
panelGrid.Background = new SolidColorBrush(mcPassColor);
panelGrid.SetValue(Grid.RowProperty, intRowProperty);
panelGrid.SetValue(Grid.ColumnProperty, intColumnProperty);
panelGrid.Visibility = vVisibility;
RowDefinition row1 = new RowDefinition();
row1.Height = new GridLength(100, GridUnitType.Auto);
panelGrid.RowDefinitions.Add(row1);
ColumnDefinition column1 = new ColumnDefinition();
column1.Width = new GridLength(100);
panelGrid.ColumnDefinitions.Add(column1);
passLayoutRoot.Children.Add(panelGrid);
}
I figured it out, I needed to create the border first then add the grid to the border.
One major difference is that I could not reference the border object directly, I needed to "find it"
Border findBorder = passLayoutRoot.FindName("bd" + strPassGridName) as Border;
if (findBorder == null)
{ }
else
{
findBorder.Child = panelGrid;
}
This worked perfectly....
Thanks to all that attempted to help
You can do it as below,
Border gridBorder = new Border();
gridBorder.BorderBrush = new SolidColorBrush(Colors.Black);
gridBorder.BorderThickness = new Thickness(4);
gridBorder.Child = new Grid(); //Your grid here
LayoutRoot.Children.Add(border); // ParentGrid(layout) holding the border
I have a XAML with 2 columns in a Grid and I have a button that when I click it, in the code behind, I set the visibility to collapse, and want to resize the other half of the screen to try to take up the whole screen. The collapsing part works, and the RHS then shifts over to the LHS, but it does not take up the entire screen. I tried using both the Auto and Star to resize in HidePlots, but it never takes the full screen. I thought if I collapsed the LHS, and set the column to * for the RHS, it would take up the whole screen. Any thoughts? Thanks.
Here's some code to make it more clear:
<Grid Grid.Row="1" x:Name="ExpandableGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="1.5*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" x:Name="TableGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<GroupBox Grid.Row="0" Grid.Column="0" x:Name="SampleViewGroupBox" Header="SampleView" HorizontalAlignment="Stretch" FontFamily="Arial" FontSize="12" Margin="5,0,5,0" >
<ContentControl Content="{Binding LayoutManager.SampleView}" Height="Auto" Width="Auto"/>
</GroupBox>
<Button x:Name="TableButton" HorizontalAlignment="Right" Content="Button" Width="15" Height="15" VerticalAlignment="Top" Margin="0,0,-2,0" Click="MaxButton_Click" Grid.Column="0" Grid.Row="0"/>
</Grid>
<Grid Grid.Column="1" x:Name="BaseViewGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<GroupBox Grid.RowSpan="2" Grid.Column="1" Name="BaseViewGroupBox" Header="PLOTS" Margin="5,0,5,0" >
<ContentControl Content="{Binding LayoutManager.ConsensusView}" Height="Auto" Width="Auto" />
</GroupBox>
</Grid>
</Grid>
private void MaxButton_Click(object sender, RoutedEventArgs e)
{
UIElement senderElement = (UIElement)sender;
if (_tableMinimized)
{
HideTables(false);
_tableMinimized = false;
((Button)senderElement).Style = (Style)FindResource("DashboardDetailsButton");
}
else
{
HideTables(true);
_tableMinimized = true;
((Button)senderElement).Style = (Style)FindResource("DashboardDetailsButtonReverse");
}
}
private void HideTables(bool hide)
{
if (hide)
{
foreach (UIElement child in TableGrid.Children)
child.Visibility = Visibility.Collapsed;
for (int i = 0; i < ExpandableGrid.ColumnDefinitions.Count; i++)
ExpandableGrid.ColumnDefinitions[i].Width = GridLength.Auto;
ExpandableGrid.ColumnDefinitions[1].MinWidth = 500;
for (int i = 0; i < ExpandableGrid.RowDefinitions.Count; i++)
ExpandableGrid.RowDefinitions[i].Height = GridLength.Auto;
TableButton.Visibility = Visibility.Visible;
}
else
{
foreach (UIElement child in TableGrid.Children)
child.Visibility = Visibility.Visible;
for (int i = 0; i < ExpandableGrid.ColumnDefinitions.Count; i++)
ExpandableGrid.ColumnDefinitions[i].Width = new GridLength(1, GridUnitType.Star);
for (int i = 0; i < ExpandableGrid.RowDefinitions.Count; i++)
ExpandableGrid.RowDefinitions[i].Height = new GridLength(1, GridUnitType.Star);
}
}
Edit: I tried to also change one line to:
ExpandableGrid.ColumnDefinitions[1].MinWidth = System.Windows.SystemParameters.PrimaryScreenWidth-20;
instead of the hard-coded 500 value, it looks correct. However, if I try to click the button again to revert back to normal, the RHS takes up the bulk of the screen without getting back to its original position.
Your current column definition says to make Column B equal to 1.5 times the size of Column A, so even if ColumnB's content is hidden, the column will still take up 3/5 of the screen.
Change it so the column that collapses has a Width="Auto", and set it's Content's Width equal to whatever size it should be when it's expanded. If you want to keep the 1.5* default width, I'd recommend using something like a MathConverter to figure out what size it should be based on the parent Grid's width. I have the code for one posted here
<Grid x:Name="ParentGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid x:Name="RHS" Grid.Column="0" />
<!-- Collapse this Grid -->
<Grid x:Name="LHS" Grid.Column="1"
Width="{Binding ElementName=ParentGrid, Path=ActualWidth,
Converter={StaticResource MathConverter},
ConverterParameter=((#VALUE/5)*3)}" />
</Grid>
You need to set column 0 to be whatever you desire (Auto, 150, etc...) and set column 1 to be *.
It looks like your Grid is also within a Grid, so the parent's behavior also has to be taken into account.