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.
Related
I'm just starting a new WPF app.
I have a grid and want to create the rows dynamically (pressing a button for example) and then create TextView/ProgressBar inside this row.
I already searched how to create the gridrows programatically. But in every solution, i can't access what's inside and it becomes useless.
<Grid x:Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button x:Name="AddLineButton" Content="Click to add a new line" Click="AddLineButton_Click"/>
<Grid x:Name="beGrid" Grid.Row="1">
<!-- I need my new rows here -->
</Grid>
</Grid>
int i = 0; //nb of rows
private void AddLineButton_Click(object sender, RoutedEventArgs e)
{
Create_line();
i++;
}
private void Create_line()
{
RowDefinition gridRow = new RowDefinition();
gridRow.Height = new GridLength(1, GridUnitType.Star);
beGrid.RowDefinitions.Add(gridRow);
StackPanel stack = new StackPanel();
stack.Orientation = Orientation.Horizontal;
TextBlock textBlock = new TextBlock();
textBlock.Text = "Question";
textBlock.Name = "Test" + i.ToString();
stack.Children.Add(textBlock);
beGrid.Children.Add(stack);
Grid.SetRow(stack, i);
}
I can't access a previously created element.
AFTER ANSWER :
private void Create_line()
{
RowDefinition gridRow = new RowDefinition();
gridRow.Height = new GridLength(1, GridUnitType.Star);
beGrid.RowDefinitions.Add(gridRow);
StackPanel stack = new StackPanel();
stack.Orientation = Orientation.Horizontal;
TextBlock textBlock = new TextBlock();
textBlock.Text = "Question";
textBlock.Name = "Test" + i.ToString();
RegisterName(textBlock.Name, textBlock);
stack.Children.Add(textBlock);
beGrid.Children.Add(stack);
Grid.SetRow(stack, i);
}
To get the created TextBlock : var text = (TextBlock)FindName("Test"+i.ToString());
you can store all created StackPanel in a List.
private void AddLineButton_Click(object sender, RoutedEventArgs e)
{
Create_line();
}
List<StackPanel> items;
private void Create_line()
{
RowDefinition gridRow = new RowDefinition();
gridRow.Height = new GridLength(1, GridUnitType.Star);
beGrid.RowDefinitions.Add(gridRow);
StackPanel stack = new StackPanel();
stack.Orientation = Orientation.Horizontal;
int i = items.Count + 1;
TextBlock textBlock = new TextBlock();
textBlock.Text = "Question";
textBlock.Name = "Test" + i.ToString();
stack.Children.Add(textBlock);
beGrid.Children.Add(stack);
Grid.SetRow(stack, items.Count);
items.Add(stack);
}
you can access any previos panel by index, e.g. items[0], and get elements from Children property: items[0].Children[0] as TextBlock
Creating controls manually like this is really not the WPF way ...
The best methodology is to define an item class that holds properties for each value that you want to display / edit.
Then create an ObservableCollection (since you will be manually adding items on a button click) of these items within your Window, and set this as the ItemsSource property of an ItemsControl control. A DataTemplate is used to define the exact controls to display each item within the control, which will bind to the properties of the item.
I am creating UWP app, and I maked external arrow "marker" of selected item in listview...
Like this:
I have managed to achieve this with next code:
var current = lvMain.Items.FirstOrDefault(a => (a as MyModel).Selected) as MyModel;
ListViewItem selected = lvMain.ContainerFromItem(current) as ListViewItem;
GeneralTransform generalTransform1 = gvEpg.TransformToVisual(selected);
Point currentPoint = generalTransform1.TransformPoint(new Point());
In Scroll change event I am calling this and set the arrow position by the Point of my item. And this is working.
But, I want to simplified this. Is there any kind of binding or something like that, that would make arrow always follow the item?
Here's the sample.
XAML MainPage:
<Page.Resources>
<DataTemplate x:Key="DataTemplate">
<Canvas Height="80" Width="200">
<TextBlock Text="{Binding}"/>
</Canvas>
</DataTemplate>
</Page.Resources>
<StackPanel Orientation="Horizontal">
<ListView x:Name="ListView" Width="400"
SelectionChanged="ListView_OnSelectionChanged"
ItemTemplate="{StaticResource DataTemplate}"/>
<Canvas x:Name="ParentCanvas">
<Image x:Name="Arrow"
Stretch="UniformToFill" Width="200" Height="80"
Source="Assets/Red_Left_Arrow.png"/>
</Canvas>
</StackPanel>
Code behind:
private readonly List<string> _names = new List<string>();
private Visual _rectangleVisual;
private Visual _parentVisual;
public MainPage()
{
InitializeComponent();
Loaded += MainPage_Loaded;
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 32; i++)
{
_names.Add("item " + i);
}
ListView.ItemsSource = _names;
_parentVisual = ElementCompositionPreview.GetElementVisual(ParentCanvas);
_rectangleVisual = ElementCompositionPreview.GetElementVisual(Arrow);
var border = VisualTreeHelper.GetChild(ListView, 0) as Border;
var scrollViewer = border.Child as ScrollViewer;
var scrollerProperties = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(scrollViewer);
var offsetExpressionAnimation = _rectangleVisual.Compositor.CreateExpressionAnimation("Scroller.Translation.Y");
offsetExpressionAnimation.SetReferenceParameter("Scroller", scrollerProperties);
_rectangleVisual.StartAnimation("Offset.Y", offsetExpressionAnimation);
}
private void ListView_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var listViewItem = ListView.ContainerFromItem(ListView.SelectedItem) as ListViewItem;
var listItemVisual = ElementCompositionPreview.GetElementVisual(listViewItem);
_parentVisual.Offset = new Vector3(_parentVisual.Offset.X, listItemVisual.Offset.Y, 0);
}
Looks like what you asked for:
I want to bind data to a grid in xaml. This is the code I am using for binding it:
<Grid x:Name="Example" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<ContentPresenter Content="{Binding ExampleImage}" />
</Grid>
Now when I bind it using fixed width and height it will display the grid at set dimensions.
Code:
private Grid _exampleImage;
public Grid ExampleImage
{
get
{
if (SelectedSectie != null)
{
var convertFromString = System.Windows.Media.ColorConverter.ConvertFromString("#CCCCCC");
if (convertFromString != null)
{
DropShadowEffect dse = new DropShadowEffect
{
BlurRadius = 5,
ShadowDepth = 1,
Direction = 270,
Color =
(System.Windows.Media.Color)
convertFromString
};
_exampleImage = new Grid
{
Background =
new SolidColorBrush(SingleIcons.Helpers.ColorConverter.ToMediaColor(SelectedSectie.Color.ColorValue)),
VerticalAlignment = VerticalAlignment.Stretch,
Width = SelectedIconSize.Width,
Height = SelectedIconSize.Height,
MaxWidth = SelectedIconSize.Width,
MaxHeight = SelectedIconSize.Height,
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
Effect = dse,
RowDefinitions =
{
new RowDefinition {Height = new GridLength(46, GridUnitType.Star)},
new RowDefinition {Height = new GridLength(3, GridUnitType.Star)}
}
};
}
TextBlock afbeeldingTextBlock = new TextBlock
{
Text = _selectedSectie.Sectie,
TextWrapping = TextWrapping.Wrap,
TextAlignment = TextAlignment.Center,
Width = _exampleImage.Width,
FontSize = Global.Fontsize,
FontFamily = new System.Windows.Media.FontFamily(Global.Family.Name),
VerticalAlignment = VerticalAlignment.Center,
Foreground =
new SolidColorBrush(
SingleIcons.Helpers.ColorConverter.ToMediaColor(SelectedSectie.Color.TextColorValue)),
};
TextOptions.SetTextFormattingMode(afbeeldingTextBlock, TextFormattingMode.Display);
TextOptions.SetTextRenderingMode(afbeeldingTextBlock, TextRenderingMode.ClearType);
Canvas bottomCanvas = new Canvas
{
Background = (SolidColorBrush) (new BrushConverter().ConvertFrom("#26000000"))
};
Grid.SetRow(afbeeldingTextBlock, 0);
Grid.SetRow(bottomCanvas, 1);
_exampleImage.Children.Add(afbeeldingTextBlock);
_exampleImage.Children.Add(bottomCanvas);
_exampleImage.Measure(new System.Windows.Size(_exampleImage.Width,
_exampleImage.Height));
_exampleImage.Arrange(
new Rect(new System.Windows.Size(_exampleImage.Width, _exampleImage.Height)));
return _exampleImage;
}
else
{
return null;
}
}
}
Now what I would like to do is to keep the grid at set dimensions but allow the GUI to scale the grid,
because when I make the window smaller the grid stays at these dimensions and it hurts the scaling of the application.
The reason I am using a databound grid is because I have another function which exports this grid to a .png.
Putting it inside a Viewbox and setting StretchDirection to DownOnly was the result i wanted.
<Grid x:Name="Example" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<Viewbox StretchDirection="DownOnly" >
<ContentPresenter Content="{Binding ExampleImage}" />
</Viewbox>
</Grid>
Problem while programmatically generating buttons in a user control of windows phone, and using this user control to mainpage.xaml but there are no buttons shown when application runs.
here is the code snippet which i am using, Thanks !
usercontrol.xaml:
<ScrollViewer >
<StackPanel x:Name="Panel">
<ContentControl x:Name="container"></ContentControl>
</StackPanel>
</ScrollViewer>
usercontrol.xaml.cs:
public LoginInterfaceControl()
{
InitializeComponent();
this.container = new ContentControl();
this.Panel = new StackPanel();
}
public LoginInterfaceControl(string Api_key)
{
InitializeComponent();
this.Panel = new StackPanel();
this.container = new ContentControl();
loginWP_DownloadString(Api_key);
}
public async void loginWP_DownloadString(string key)
{
InitializeComponent();
string cont;
using (HttpClient client = new HttpClient())
{
var result = await client.GetAsync("http://cdn.loginradius.com/interface/json/" + key + ".json");
if (result.StatusCode == HttpStatusCode.OK)
{
cont = await result.Content.ReadAsStringAsync();
MessageBox.Show(cont);
}
else
{
cont = await result.Content.ReadAsStringAsync();
MessageBox.Show(cont);
}
if (!string.IsNullOrEmpty(cont))
{
var root1 = JsonConvert.DeserializeObject<RootObject>(cont);
int no = 1;
foreach (var provider in root1.Providers)
{
no++;
Button newBtn = new Button()
{
Content = provider.Name.ToString(),
Name = provider.Name.ToString(),
//Width = 88,
//Height = 77,
Visibility = System.Windows.Visibility.Visible,
//Margin = new Thickness(5 + 20, 5, 5, 5),
Background = new SolidColorBrush(Colors.Black),
VerticalAlignment =VerticalAlignment.Center,
Opacity=0.5
};
newBtn.Click += google_click;
System.Windows.Visibility.Visible;
container.Opacity = 0.5;
this.container.Content = newBtn;
}
}
}
Mainpage.xaml:
<Grid xmlns:src="clr-namespace:LRDemo"
Background="White" Margin="10,0,-10,186" Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<src:LoginInterfaceControl Grid.Row="0"/>
<!--<src:LoginInterfaceControl Grid.Row="1" Margin="0,15,0,0"/>-->
</Grid>
usercontrol.xaml
<ScrollViewer>
<ContentControl x:Name="container">
<StackPanel x:Name="Panel">
</StackPanel>
</ContentControl>
</ScrollViewer>
there is no need to again create stackpanel and contentcontrol in constructor of usercontrol because they are already there in usercontrol structure.
Contentcontrol can hold content that is assign to it last so I took stackpanel into contentcontol.
usercontrol.xaml.cs
public LoginInterfaceControl()
{
this.InitializeComponent();
abc();
}
public void abc()
{
for (int i = 0; i <= 5; i++)
{
Button newBtn = new Button()
{
Content = "name" + i,
Name = "name" + i,
Background = new SolidColorBrush(Colors.Black),
VerticalAlignment = VerticalAlignment.Center,
Opacity = 0.5
};
newBtn.Click += newBtn_Click;
container.Opacity = 0.5;
this.Panel.Children.Add(newBtn);
}
}
P.S : I do not know your exact need so I took static methods to add buttons.
i am creating a Button and a textbox dynamically one by one in grid. My requirement is, if i click the button, the popup will show. and if i select the values from popup, i need to bind the corresponding row's textbox. Fr example, if i click the 5th row's button, i need to bind the popup item value to the 5th rows textbox. i struck on binding values to corresponding row's textbox. this may be simple one but i am unable to done this. this is my code.,
Xaml:
<Button x:Name="btn_addnewrow" Content="Add" HorizontalAlignment="Left" Margin="50,40,0,0" VerticalAlignment="Top" Width="89" Height="31" Click="btn_addnewrow_Click"/>
<Popup Name="popup" IsOpen="False" Placement="Mouse" VerticalOffset="15" HorizontalOffset="0" Margin="124,122,107,65">
<Border BorderBrush="Black" BorderThickness="1" Background="Coral">
<StackPanel Orientation="Horizontal" Height="143">
<ListView Margin="10,10,0,0" Name="ListView1" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="194" Height="133" MouseDoubleClick="ListView1_MouseDoubleClick">
<ListViewItem Content="Coffie"></ListViewItem>
<ListViewItem Content="Tea"></ListViewItem>
<ListViewItem Content="Orange Juice"></ListViewItem>
<ListViewItem Content="Milk"></ListViewItem>
<ListViewItem Content="Iced Tea"></ListViewItem>
<ListViewItem Content="Mango Shake"></ListViewItem>
</ListView>
</StackPanel>
</Border>
</Popup>
cs:
public int count = 0;
public Button btn1;
public Button btn2;
public TextBox txt1;
private void btn_addnewrow_Click(object sender, RoutedEventArgs e)
{
//Creating Rows..
RowDefinition row0 = new RowDefinition();
row0.Height = new GridLength(40);
grid1.RowDefinitions.Add(row0);
//Creating columns..
ColumnDefinition col0 = new ColumnDefinition();
ColumnDefinition col1 = new ColumnDefinition();
ColumnDefinition col2 = new ColumnDefinition();
col0.Width = new GridLength(50);
col1.Width = new GridLength(100);
col2.Width = new GridLength(70);
grid1.ColumnDefinitions.Add(col0);
grid1.ColumnDefinitions.Add(col1);
grid1.ColumnDefinitions.Add(col2);
int i = count;
////1st Column button
btn1 = new Button();
btn1.Margin = new Thickness(10, 10, 0, 0);
btn1.BorderThickness = new Thickness(0);
Grid.SetRow(btn1, i);
Grid.SetColumn(btn1, 0);
btn1.Tag = btn1;
btn1.Click += btnBindList_Click;
grid1.Children.Add(btn1);
//2nd column Textbox
txt1 = new TextBox();
txt1.Margin = new Thickness(10, 10, 0, 0);
txt1.Name = "txt" + i;
Grid.SetRow(txt1, i);
Grid.SetColumn(txt1, 1);
txt1.Tag = txt1;
grid1.Children.Add(txt1);
count++;
}
private void btnBindList_Click(object sender, RoutedEventArgs e)
{
?
?
popup.IsOpen = true;
}
private void ListView1_MouseDoubleClick(object sender, RoutedEventArgs e)
{
txt1.Text = (ListView1.SelectedItem as ListViewItem).Content.ToString();
popup.IsOpen = false;
}
Create a usercontrol with button, textbox and popup. This usercontrol should set to the cell template or item template of gridview or listview. The textbox is bound to selected value of popup listview. So when user open popup and choose the value, the selected value will update in corresponding textbox.
<Grid HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Text="{Binding ElementName=ListView1, Path=SelectedValue.Content}" Width="200" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="2"/>
<ToggleButton Content="Select" Grid.Column="1" HorizontalAlignment="Left" Margin="2" x:Name="btn"/>
<Popup PlacementTarget="{Binding ElementName=btn}" Placement="Bottom"
StaysOpen="False"
IsOpen="{Binding ElementName=btn, Path=IsChecked}">
<ListView Name="ListView1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="194"
Height="133">
<ListViewItem Content="Coffie"></ListViewItem>
<ListViewItem Content="Tea"></ListViewItem>
<ListViewItem Content="Orange Juice"></ListViewItem>
<ListViewItem Content="Milk"></ListViewItem>
<ListViewItem Content="Iced Tea"></ListViewItem>
<ListViewItem Content="Mango Shake"></ListViewItem>
</ListView>
</Popup>
</Grid>
If you want to use Binding, you need to creat a viewmodel,a class. And binding it to Button and TextBox. When you click a button,get the viewmodel, and set it's value by selecting in listview.
The code is very confused, but you can debug and rewrite it by yourself.
public int count = 0;
public Button btn1;
public Button btn2;
public TextBox txt1;
private void btn_addnewrow_Click(object sender, RoutedEventArgs e)
{
//Creating Rows..
RowDefinition row0 = new RowDefinition();
row0.Height = new GridLength(40);
grid1.RowDefinitions.Add(row0);
//Creating columns..
ColumnDefinition col0 = new ColumnDefinition();
ColumnDefinition col1 = new ColumnDefinition();
ColumnDefinition col2 = new ColumnDefinition();
col0.Width = new GridLength(50);
col1.Width = new GridLength(100);
col2.Width = new GridLength(70);
grid1.ColumnDefinitions.Add(col0);
grid1.ColumnDefinitions.Add(col1);
grid1.ColumnDefinitions.Add(col2);
int i = count;
Test t = new Test();
////1st Column button
btn1 = new Button();
btn1.Margin = new Thickness(10, 10, 0, 0);
btn1.BorderThickness = new Thickness(0);
Grid.SetRow(btn1, i);
Grid.SetColumn(btn1, 0);
Binding binding = new Binding();
binding.Source = t;
btn1.SetBinding(Button.TagProperty, binding);
btn1.Click += btnBindList_Click;
grid1.Children.Add(btn1);
Binding binding1 = new Binding("Content");
binding1.Source = t;
//2nd column Textbox
txt1 = new TextBox();
txt1.Margin = new Thickness(10, 10, 0, 0);
txt1.Name = "txt" + i;
txt1.SetBinding(TextBox.TextProperty, binding1);
Grid.SetRow(txt1, i);
Grid.SetColumn(txt1, 1);
txt1.Tag = txt1;
grid1.Children.Add(txt1);
count++;
}
private void btnBindList_Click(object sender, RoutedEventArgs e)
{
popup.IsOpen = true;
t = ((Button)sender).Tag as Test;
}
Test t;
private void ListView1_MouseDoubleClick(object sender, RoutedEventArgs e)
{
t.Content = (ListView1.SelectedItem as ListViewItem).Content.ToString();
popup.IsOpen = false;
}
}
class Test : INotifyPropertyChanged
{
private string content;
public string Content
{
get { return content; }
set
{
content = value;
OnPropertyChanged("Content");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}