WPF. How to dynamically add new pages to grid? - c#

I am creating app in wpf that display some places in the world (descriptions, photos) and there are also opinions, that user can write. To display them I created new Grid, where user can add up to 5 opinions (if more the texts overlap). Because of this problem I wanted to dynamically add new pages to Grid, e.g. if user added 6th opinion, new grid page created and new button appers on the bottom of Grid with page number (navigation). But I have no clue how to write it.
Grid used to display opinions:
<Grid Grid.Column="2" Grid.ColumnSpan="3"
Grid.Row="0" Grid.RowSpan="3" x:Name="newGrid" Margin="0,50,0,0"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
</Grid>
Function used to add new opinions:
private void btnConfirm_Click(object sender, RoutedEventArgs e)
{
txtName.Visibility = Visibility.Hidden;
tbName.Visibility = Visibility.Hidden;
txtOpinion.Visibility = Visibility.Hidden;
tbOpinion.Visibility = Visibility.Hidden;
btnConfirm.Visibility = Visibility.Hidden;
var name = tbName.Text;
var opinion = tbOpinion.Text;
if(name != "" && opinion != "" && name != " " && opinion != " ")
{
TextBlock tb = new TextBlock();
tb.Text = name + ": " + opinion;
tb.HorizontalAlignment = HorizontalAlignment.Left;
tb.VerticalAlignment = VerticalAlignment.Bottom;
tb.FontSize = 30;
tb.Foreground = Brushes.White;
tb.Margin = new Thickness(50, 0, 0, 0);
tb.TextWrapping = TextWrapping.Wrap;
tb.TextAlignment = TextAlignment.Justify;
newGrid.Children.Add(tb);
Grid.SetRow(tb, klik);
Grid.SetColumnSpan(tb, 3);
klik++; //opinion counter and also number of row
}
else
{
MessageBox.Show("Fields cannot by empty!","Error",MessageBoxButton.OK,MessageBoxImage.Error);
}
I suppose variable klik must be used, because that variable is responsible for counting opinions. So it would be:
if(klik>5)
{
//add new grid page - but I have no clue how to do this
}

First off I want to preface this by saying there are many better ways to design your application (using MVVM design patterns, Styles, DataTemplates, etc.). Learning those skills will benefit you as a developer in the long run. Having said that, I'll answer this based on your request.
Because you are adding new rows that do not have a home yet on your Grid, you'll first need to add a new placeholder for the row by adding a new RowDefinition.
newGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
Then perform your code the same as you did before. So it should look something like this:
if(klik>5)
{
newGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
}
if(name != "" && opinion != "" && name != " " && opinion != " ")
{
TextBlock tb = new TextBlock();
tb.Text = name + ": " + opinion;
tb.HorizontalAlignment = HorizontalAlignment.Left;
// ... etc etc etc
}

Related

WPF Drag Control during runtime

I been working with winforms, so my knowledge of WFP is non existent, this is something i am trying to test.
In code I am generating few buttons and placing them on Canvas. Than after clik on any button, i am moving that button around, and after second click button should stay at the position where mouse cursor was when clicked.
If mouse cursor go outside canvas then button will stop follow it.
My problem is, that button is moving, but only when mouse cursor is over that button or any other control, but it is not moving while mouse cursor is traveling over Canvas.
XAML
<Window x:Class="WpfTestDrag.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:WpfTestDrag"
mc:Ignorable="d"
Title="MainWindow" Height="522" Width="909">
<Grid>
<Grid.ColumnDefinitions >
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="130*"/>
<ColumnDefinition Width="33*"/>
<ColumnDefinition Width="120"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions >
<RowDefinition Height="40" />
<RowDefinition Height="*" />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<Canvas Grid.Column="1" Grid.Row="1" x:Name="cnvTest" Width="auto" Height="auto" PreviewMouseMove="CnvTest_PreviewMouseMove"/>
<TextBlock x:Name="txbStatus" Grid.Column="1" Grid.Row="2" Grid.ColumnSpan="2"/>
</Grid>
</Window>
C#
public partial class MainWindow : Window
{
Button bts;
Boolean isUsed = false;
public MainWindow()
{
InitializeComponent();
CreateButtons();
}
private void CreateButtons()
{
var xPos = 10.0;
var yPos = 15.0;
var Rnd = new Random();
for (int i = 0; i < 3; i++)
{
var btn = new Button();
btn.Name = "btn" + i;
btn.Content = "Button - " + i;
btn.Tag = "Tag" + i;
btn.Width = 150;
btn.Height = 150;
btn.Click += Btn_Click;
Canvas.SetLeft(btn, xPos);
Canvas.SetTop(btn, yPos);
cnvTest.Children.Add(btn);
xPos = xPos + btn.Width + Rnd.Next(-15,40);
yPos = yPos + btn.Height + Rnd.Next(-15, 40);
}
}
private void Btn_Click(object sender, RoutedEventArgs e)
{
bts = sender as Button;
if (isUsed == false)
{
isUsed = true;
}
else
{
isUsed = false;
}
}
private void CnvTest_PreviewMouseMove(object sender, MouseEventArgs e)
{
Point p = Mouse.GetPosition(cnvTest);
if (isUsed == true)
{
Canvas.SetLeft(bts, p.X);
Canvas.SetTop(bts, p.Y);
txbStatus.Text = bts.Name.ToString() + " isUsed:" + isUsed.ToString() + " -> xPos:" + p.X.ToString() + " yPos:" + p.Y.ToString();
}
}
}
Should I use something else than Canvas for this?
You should set the Background property of the Canvas to Transparent (or any other Brush) for it to respond to the mouse events:
<Canvas Grid.Column="1" Grid.Row="1" x:Name="cnvTest" Width="auto" Height="auto" PreviewMouseMove="CnvTest_PreviewMouseMove"
Background="Transparent"/>

Grid is getting smaller after pressing "AddLexemesFromFolder" button

I develop one-page app. This is XAML of MainPage
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical" Grid.Row="0">
<AppBar x:Name="MenuAppBar" IsOpen="True">
<StackPanel Orientation="Horizontal">
<AppBarButton Icon="Add" Label="Добавить лексемы" Name="AddLexemesFromFolder" Click="OpenFolderAndGetLexemes_Click" HorizontalAlignment="Left"/>
<AppBarButton Icon="Save" Label="Сохранить лексемы" Name="SaveLexemes" Click="SaveLexemes_Click" HorizontalAlignment="Left"/>
</StackPanel>
</AppBar>
</StackPanel>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" VerticalScrollMode="Enabled">
<Grid x:Name="GridLexemesViewer" HorizontalAlignment="Stretch"/>
</ScrollViewer>
</Grid>
When I pressed "AddLexemesFromFolder" button more than two times, GridLexemesViewer is getting smaller over and over.
This is OpenFolderAndGetLexemes code
private async void OpenFolderAndGetLexemes_Click(object sender, RoutedEventArgs routedEventArgs)
{
await StartSaveLexemes();
var folderPicker = new Windows.Storage.Pickers.FolderPicker();
folderPicker.FileTypeFilter.Add("*");
Windows.Storage.StorageFolder folder = await folderPicker.PickSingleFolderAsync();
if (folder != null)
{
StorageApplicationPermissions.FutureAccessList.AddOrReplace("PickedFolderToken", folder);
await Task.Run(() => StartNewSessionForGetLexemes(folder.Path));
InitializeGrid();
}
}
I use "InitializeGrid" method for clear Children in GridLexemesViewer, use CreateRowsAndColumns and put TextBox with content to GridLexemesViewer.
This is code of InitializeGrid and CreateRowsAndColumns()
private void InitializeGrid()
{
GridLexemesViewer.Children.Clear();
CreateRowsAndColumns();
int index = 1;
foreach (var lexem in CurrentSession.Lexemes)
{
foreach (var item in lexem.Value)
{
Binding binding = new Binding
{
Source = item,
Path = new PropertyPath("Value"),
Mode = BindingMode.TwoWay,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
TextBox textBox = new TextBox { TextWrapping = TextWrapping.Wrap };
BindingOperations.SetBinding(textBox, TextBox.TextProperty, binding);
GridLexemesViewer.Children.Add(textBox);
Grid.SetColumn(textBox, CurrentSession.Languages.IndexOf(item.Language) + 1);
Grid.SetRow(textBox, index);
}
index++;
}
}
private void CreateRowsAndColumns()
{
int indexRow = 1;
int indexColumn = 1;
RowDefinition firstRowDefinition = new RowDefinition();
ColumnDefinition firstColumnDefinition = new ColumnDefinition { Width = GridLength.Auto };
GridLexemesViewer.ColumnDefinitions.Add(firstColumnDefinition);
GridLexemesViewer.RowDefinitions.Add(firstRowDefinition);
foreach (var key in CurrentSession.Lexemes.Keys)
{
RowDefinition rowDefinition = new RowDefinition();
GridLexemesViewer.RowDefinitions.Add(rowDefinition);
TextBlock textBlock = new TextBlock{Text = key};
GridLexemesViewer.Children.Add(textBlock);
Grid.SetRow(textBlock, indexRow);
indexRow++;
}
foreach (var language in CurrentSession.Languages)
{
ColumnDefinition columnDefinition = new ColumnDefinition { Width = new GridLength(1.0, GridUnitType.Star)};
GridLexemesViewer.ColumnDefinitions.Add(columnDefinition);
TextBlock textBlock = new TextBlock {Text = language};
GridLexemesViewer.Children.Add(textBlock);
Grid.SetRow(textBlock, 0);
Grid.SetColumn(textBlock, indexColumn);
indexColumn++;
}
}
This GIF shows how to reproduce bug
The problem is that you are calling CreateRowsAndColumns() each time but not removing the Rows and Columns from previous run. Using Grid.Clear() only deletes the children controls in the Grid, but the Grid.RowDefinitions and Grid.ColumnDefinitions stay intact.
To fix this, clear both definitions at the start of CreateRowsAndColumns():
GridLexemesViewer.RowDefinitions.Clear();
GridLexemesViewer.ColumnDefinitions.Clear();
However, definitely consider using the DataGrid control from the Windows Community Toolkit as it should have all the features you need and has better maintainability and performance then a custom Grid, especially for bigger data.

Exception in Main Window while SizeChanged event used

I have wpf solution, where I have created UserControl for trending. This UserControl is used in MainWindow.
The path of trend is painted on method showData() of current class ChartControl. But because I want to have actual picture of path related to main window size, I have add SizeChanged event where this showData() method is called.
My code for event here:
private void OnResize(object sender, SizeChangedEventArgs e)
{
this.showData();
}
Edit:
private List<ChartData> data = new List<ChartData>();
public void showData()
{
double maxVal = this.maxVal();
double minVal = this.minVal();
TimeSpan timeSpan = new TimeSpan();
timeSpan = this.maxTime() - this.minTime();
double stepSize = Area.ActualWidth / timeSpan.TotalSeconds;
setLabels();
Area.Children.Clear();
for (int i = 1; i < this.data.Count; i++)
{
Line lineHorizont = new Line();
lineHorizont.StrokeThickness = 2;
lineHorizont.Stroke = Brushes.Red;
lineHorizont.X1 = (this.data[i].X - this.minTime()).TotalSeconds * stepSize;
lineHorizont.Y1 = Math.Abs(((this.data[i - 1].Y - minVal) / (maxVal - minVal) * Area.ActualHeight) - Area.ActualHeight);
lineHorizont.X2 = lineHorizont.X1;
lineHorizont.Y2 = Math.Abs(((this.data[i].Y - minVal) / (maxVal - minVal) * Area.ActualHeight) - Area.ActualHeight);
Area.Children.Add(lineHorizont);
Line lineVertical = new Line();
lineVertical.StrokeThickness = 2;
lineVertical.Stroke = Brushes.Red;
lineVertical.X1 = (this.data[i - 1].X - this.minTime()).TotalSeconds * stepSize;
lineVertical.Y1 = Math.Abs(((this.data[i - 1].Y - minVal) / (maxVal - minVal) * Area.ActualHeight) - Area.ActualHeight);
lineVertical.X2 = (this.data[i].X - this.minTime()).TotalSeconds * stepSize;
lineVertical.Y2 = lineVertical.Y1;
Area.Children.Add(lineVertical);
}
//Draw cross coordinator
coordX1.StrokeThickness = 1;
coordX1.Stroke = Brushes.Black;
coordX1.X1 = 0;
coordX1.Y1 = Mouse.GetPosition(Area).Y;
coordX1.X2 = Area.ActualWidth;
coordX1.Y2 = coordX1.Y1;
Area.Children.Add(coordX1);
coordX2.StrokeThickness = 1;
coordX2.Stroke = Brushes.Black;
coordX2.X1 = Mouse.GetPosition(Area).X;
coordX2.Y1 = 0;
coordX2.X2 = coordX2.X1;
coordX2.Y2 = Area.ActualHeight;
Area.Children.Add(coordX2);
}
public double maxVal()
{
List<double> data = new List<double>();
for (int i = 0; i < this.data.Count; i++)
{
data.Add(this.data[i].Y);
}
return data.Max<double>();
}
edit2:
xaml content of Main Window
<Grid Margin="0">
<lib:ChartControl x:Name="Trend" Margin="0" Width="Auto" Height="Auto"/>
</Grid>
xaml content of ChartControl
<Grid Grid.Column="1" Margin="0" Grid.Row="1" Background="Black" Cursor="Cross" PreviewMouseMove="OnMouseMove">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Rectangle Fill="#FFF1F1F1" Grid.RowSpan="10"/>
<Rectangle Fill="#FFD4D4D4"/>
<Rectangle Fill="#FFD4D4D4" Grid.Row="2"/>
<Rectangle Fill="#FFD4D4D4" Grid.Row="4"/>
<Rectangle Fill="#FFD4D4D4" Grid.Row="6"/>
<Rectangle Fill="#FFD4D4D4" Grid.Row="8"/>
<Canvas x:Name="Area" Grid.RowSpan="10"/>
</Grid>
After start of program everything is working fine and according expectations, but I am getting Exception in View Designer in Visual studio, which is eliminating any design changes.
In the OnResize event handler you must test that the current instance is not in design mode.
System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)
if you have no data then return...then no more design exception !!
private void OnResize(object sender, SizeChangedEventArgs e) {
if(!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
this.showData();
}
List<double> data = new List<double>();
for (int i = 0; i < this.data.Count; i++)
{
data.Add(this.data[i].Y);
}
return data.Max<double>();
Please, please, please don't name your local variables the same as your outer-scope variables.
As for why this is failing, it's obvious:
this.data is empty when this is called, as such, when you try to perform data.Max<double>() you get an exception.
You're calling MaxVal() at the very beginning of ShowData() and as far as I can see, there's no real discernible place you're adding any ChartData to the data list.

Dynamically creating buttons in Windows Phone 8.1

I created app in Windows Phone 8.1(universal). I need to create a dynamic field of the buttons. This is my example xaml:
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid x:Name="_root"
Grid.Row="1"
Margin="10" />
This is my code:
_root.Children.Clear();
_root.ColumnDefinitions.Clear();
Size = 5;
for (int i = 0; i < Size; i++)
{
_root.ColumnDefinitions.Add(new ColumnDefinition
{
Width = new GridLength(1, GridUnitType.Star)
});
}
for (int column = 0; column < Size; column++)
{
Button btnTemp = new Button();
btnTemp.Visibility = Visibility.Visible;
btnTemp.VerticalAlignment = VerticalAlignment.Stretch;
btnTemp.HorizontalAlignment = HorizontalAlignment.Stretch;
btnTemp.HorizontalContentAlignment = HorizontalAlignment.Center;
btnTemp.VerticalContentAlignment = VerticalAlignment.Center;
btnTemp.BorderBrush = new SolidColorBrush(Colors.Blue);
btnTemp.Content = column;
Grid.SetColumn(btnTemp, column);
Grid.SetRow(btnTemp, 0);
_root.Children.Add(btnTemp);
}
It is my result:
If I used :
Width = new GridLength(1, GridUnitType.Auto)
I get result:
I need to fix these bugs:
1.Align text to the center of the button.
Do not see the right border of the button "4".Button "4" other widely.
Fix in your case is quite easy - just define MinWidth of created button as 0 (default has some value).
btnTemp.MinWidth = 0;
I think it should help.
Nevertheless, in your case I wouldn't add buttons to grid, I would probably define ItemsPanel and suitable ItemsTemplate with Button, then bind to a collection.

how to create listview to a grid programmatically

I have a grid with columns and rows define. By knowing x amount of items, I can add it to my xaml no problem. Unfortuantely I want to create the listview programatically because I want to populate them based on x amount of item I get when executing my SP. Here is my xaml. The label is there to assign a given content from the SP result too. Can someone show me how create this programatically?
<Grid Name="grdItems" Width="939" Grid.Row="1" HorizontalAlignment="Left" DataContext="{Binding}" Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="25"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="5"></RowDefinition>
<RowDefinition Height="25"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="5"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="231"></ColumnDefinition>
<ColumnDefinition Width="5"></ColumnDefinition>
<ColumnDefinition Width="231"></ColumnDefinition>
<ColumnDefinition Width="5"></ColumnDefinition>
<ColumnDefinition Width="231"></ColumnDefinition>
<ColumnDefinition Width="5"></ColumnDefinition>
<ColumnDefinition Width="231"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Name="lblItem" Grid.Row="0" Grid.Column="0" Content="item label" />
<ListView Name="lstFirstItem" HorizontalAlignment="Left" VerticalAlignment="Top"
Height="auto" Margin="0,0,0,0" ItemsSource="" FontWeight="Regular"
Grid.Column="0" Grid.Row="1">
<ListView.View>
<GridView>
<GridView.ColumnHeaderContainerStyle>
<Style>
<Setter Property="FrameworkElement.Visibility" Value="Collapsed"/>
</Style>
</GridView.ColumnHeaderContainerStyle>
<GridViewColumn Width="58" DisplayMemberBinding="{Binding ItemName}"></GridViewColumn>
<GridViewColumn Width="174.25" DisplayMemberBinding="{Binding ItemDescription}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
we can add rows or columns dynamically from code behind.
Inorder to add rows:
GridLength rowheight = new GridLength(100); //your own value we can give *,Auto as well
RowDefinition rowDef = new RowDefinition {Height = rowheight };
mainGrid.RowDefinitions.Add(rowDef);
Inorder to add colomns:
GridLength columnwidth = new GridLength(100);
ColumnDefinition colDef = new ColumnDefinition { Width = columnwidth};
mainGrid.ColumnDefinitions.Add(colDef );
Inorder to add textblock at particular row,column
TextBlock textBlock = new TextBlock();
textBlock.Text = "Some Value";
Grid.SetRow(textBlock, rowNum);
Grid.SetColumn(textBlock, colNum);
mainGrid.Children.Add(textBlock);
Hope this answers your question
This work perfectly for me:
ListView Lv_Report = new ListView();
Lv_Report.Name = "Lv_Report";
Lv_Report.FontSize = 14;
Lv_Report.Height = 300;
Lv_Report.Width = 800;
Canvas.SetTop(Lv_Report, 10);
Canvas.SetLeft(Lv_Report, 30);
Cv_Scheduler.Children.Add(Lv_Report);
Lv_Report.Background = null;
LinearGradientBrush myLinearGradientBrush = new LinearGradientBrush();
myLinearGradientBrush.StartPoint = new System.Windows.Point(0, 0);
myLinearGradientBrush.EndPoint = new System.Windows.Point(0, 1);
Color color = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#7FFFFFFF");
myLinearGradientBrush.GradientStops.Add(new GradientStop(color, 0.02));
color = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#BFBADF69");
myLinearGradientBrush.GradientStops.Add(new GradientStop(color, 0.19));
color = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#7FF9FCF2");
myLinearGradientBrush.GradientStops.Add(new GradientStop(color, 1));
color = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#BEB9DE67");
myLinearGradientBrush.GradientStops.Add(new GradientStop(color, 0.83));
Style style = new Style(typeof(GridViewColumnHeader));
style.Setters.Add(new Setter()
{
Property = GridViewColumnHeader.BackgroundProperty,
Value = myLinearGradientBrush
});
var Lvitems = new List<string>();
Lvitems.Add("WorkOrder");
Lvitems.Add("Module1");
GridView Gv = new GridView();
foreach (String item in Lvitems)
{
DataTemplate Dtemplate = new DataTemplate();
var markup =
"<DataTemplate xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:local=\"clr-namespace:SubMeteringElectric;assembly=SubMeteringElectric\">"
+ "<StackPanel>"
+ "<Label Content =\"{Binding Lv_RSH_" + item + "}\" Width = \"100\" Height = \"30\" HorizontalContentAlignment = \"Center\" FontSize = \"14\" />"
+ "</StackPanel>"
+ "</DataTemplate>";
byte[] byteArray = Encoding.UTF8.GetBytes(markup);
MemoryStream stream = new MemoryStream(byteArray);
Dtemplate = (DataTemplate)XamlReader.Load(stream);
GridViewColumn Gvc_item = new GridViewColumn();
Gvc_item.Header = item;
Gvc_item.Width = 150;
Gvc_item.CellTemplate = Dtemplate;
Gv.Columns.Add(Gvc_item);
}
Gv.ColumnHeaderContainerStyle = style;
Lv_Report.View = Gv;
}

Categories