I have a WPF RichTextBox with some standard buttons for text styling. As they should all work the same way, I am going to use the example of making text bold from now on.
So, my goal is, that the user may click the bold-button and then, the text he writes will be bold from wherever the caret was at this point.
The styling has to be set in the inlines of the flow document (because I have to transfer the whole text, including the styling, to another object as xaml string).
At this point, I'm going to post my code. Please note, that this is a simple test project I created to test ways to solve this specific problem.
First the xaml:
<Window x:Class="rtbTest.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:rtbTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="180"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<DockPanel
Grid.Row="0"
LastChildFill="False">
<StackPanel
DockPanel.Dock="Left"
Orientation="Horizontal"
Background="Gray">
<TextBlock
Foreground="#000000"
FontWeight="Bold"
VerticalAlignment="Center"
Text="Font"
/>
<ComboBox
x:Name="fonts"
Margin="10,2,20,2"
>
</ComboBox>
<TextBlock
Foreground="#000000"
FontWeight="Bold"
VerticalAlignment="Center"
Text="Size"
/>
<ComboBox
x:Name="fontsize"
Margin="10,2,20,2"
>
</ComboBox>
<ToggleButton
x:Name="bold"
Content="B"
Margin="0,2,5,2"
Width="30">
</ToggleButton>
<ToggleButton
x:Name="italic"
Content="I"
Margin="0,2,5,2"
Width="30">
</ToggleButton>
</StackPanel>
</DockPanel>
<RichTextBox
Name="textbox"
Grid.Row="1" Grid.ColumnSpan="2" >
</RichTextBox>
<ScrollViewer
Grid.Row="2"
Grid.ColumnSpan="2"
Margin="0,10,0,0"
VerticalScrollBarVisibility="Auto">
<StackPanel
Name="entrys"
Background="Black"
>
</StackPanel>
</ScrollViewer>
</Grid>
</Window>
And then the code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using function = System.String;
namespace rtbTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.InitializeTextStyling();
}
private void InitializeTextStyling()
{
this.AddFonts();
this.fonts.SelectedIndex = 1;
this.textbox.IsInactiveSelectionHighlightEnabled = true;
this.fonts.SelectionChanged += (s, a) =>
{
if (this.fonts.SelectedIndex != 0)
{
if (this.textbox.Selection.IsEmpty)
{
this.textbox.Document.FontFamily = ((ComboBoxItem)this.fonts.SelectedItem).Content as FontFamily;
}
else
{
this.textbox.Selection.ApplyPropertyValue(TextBlock.FontFamilyProperty, ((ComboBoxItem)this.fonts.SelectedItem).Content as FontFamily);
}
}
};
for (double i = 10; i < 80; i = i + 1)
{
this.fontsize.Items.Add(i);
}
this.fontsize.SelectionChanged += (s, a) =>
{
if (this.textbox.Selection.IsEmpty)
{
this.textbox.Document.FontSize = double.Parse(this.fontsize.SelectedItem.ToString());
}
else
{
this.textbox.Selection.ApplyPropertyValue(TextBlock.FontSizeProperty, double.Parse(this.fontsize.SelectedItem.ToString()));
}
};
this.fontsize.SelectedItem = 40.0;
this.italic.Checked += (s, a) =>
{
if (this.textbox.Selection.IsEmpty)
{
this.textbox.Document.FontStyle = FontStyles.Italic;
}
else
{
this.textbox.Selection.ApplyPropertyValue(TextBlock.FontStyleProperty, FontStyles.Italic);
}
};
this.italic.Unchecked += (s, a) =>
{
if (this.textbox.Selection.IsEmpty)
{
this.textbox.Document.FontStyle = FontStyles.Normal;
}
else
{
this.textbox.Selection.ApplyPropertyValue(TextBlock.FontStyleProperty, FontStyles.Normal);
}
};
this.bold.Checked += (s, a) =>
{
this.textbox.Focus();
if (this.textbox.Selection.IsEmpty)
{
TextPointer tp = this.textbox.CaretPosition;
Run r = new Run("test", tp);
r.FontWeight = FontWeights.Bold;
this.textbox.CaretPosition = r.ElementStart;
r.Text = "";
string fds = XamlWriter.Save(this.textbox.Document);
}
else
{
this.textbox.Selection.ApplyPropertyValue(TextBlock.FontWeightProperty, FontWeights.Heavy);
}
};
this.bold.Unchecked += (s, a) =>
{
if (this.textbox.Selection.IsEmpty)
{
this.textbox.Document.FontWeight = FontWeights.Normal;
}
else
{
this.textbox.Selection.ApplyPropertyValue(TextBlock.FontWeightProperty, FontWeights.Normal);
}
};
FlowDocument fd = new FlowDocument();
fd.FontSize = double.Parse(this.fontsize.SelectedItem.ToString());
fd.FontFamily = ((ComboBoxItem)this.fonts.SelectedItem).Content as FontFamily;
fd.FontWeight = FontWeights.Normal;
this.textbox.Document = fd;
}
private function GetStringFromStream(Stream stream)
{
using (StreamReader r = new StreamReader(stream))
{
return r.ReadToEnd();
}
}
private void AddFonts()
{
//foreach (System.Drawing.FontFamily f in System.Drawing.FontFamily.Families)
//{
// ComboBoxItem item = new ComboBoxItem();
// item.Content = new FontFamily(f.GetName(0));
// item.FontFamily = (FontFamily)item.Content;
// this.fonts.Items.Add(item);
//}
ComboBoxItem item = new ComboBoxItem();
item.Content = new FontFamily("Arial");
item.FontFamily = (FontFamily)item.Content;
this.fonts.Items.Add(item);
item = new ComboBoxItem();
item.Content = new FontFamily("Corbel");
item.FontFamily = (FontFamily)item.Content;
this.fonts.Items.Add(item);
item = new ComboBoxItem();
item.Content = new FontFamily("Gabriola");
item.FontFamily = (FontFamily)item.Content;
this.fonts.Items.Add(item);
item = new ComboBoxItem();
item.Content = new FontFamily("MingLiU - ExtB");
item.FontFamily = (FontFamily)item.Content;
this.fonts.Items.Add(item);
item = new ComboBoxItem();
item.Content = new FontFamily("MV Boli");
item.FontFamily = (FontFamily)item.Content;
this.fonts.Items.Add(item);
item = new ComboBoxItem();
item.Content = new FontFamily("Noto Naskh Arabic UI");
item.FontFamily = (FontFamily)item.Content;
this.fonts.Items.Add(item);
}
}
}
This code should be enough to test straight away.
Now, my problem is in this part:
this.bold.Checked += (s, a) =>
{
this.textbox.Focus();
if (this.textbox.Selection.IsEmpty)
{
TextPointer tp = this.textbox.CaretPosition;
Run r = new Run("test", tp);
r.FontWeight = FontWeights.Bold;
this.textbox.CaretPosition = r.ElementStart;
r.Text = "";
string fds = XamlWriter.Save(this.textbox.Document);
}
else
{
this.textbox.Selection.ApplyPropertyValue(TextBlock.FontWeightProperty, FontWeights.Heavy);
}
};
What i actually need to do, is insert a Run with no text. But if i do this, a Run is inserted in this manner <Run FontWeight="bold" /> instead of <Run FontWeight="bold"></Run>.
I can get the second one (the one I need) when I use the constructor with an initial text like "test" and then I also can set the caret position to the newly created Run. But then i have the text in my RTB, which I don't want. I thought of setting the text in the constructor and later set the text to empty, but if I do so the inserted Run reverts again to the first version and I can't set the caret position inside the Run.
Is there a way to achieve this: insert a new empty run at caret position with the text styled bold and the caret position set inside this new run?
Note that the fds string was set only so I can see the flow document as XAML string while debugging.
Related
I created a two thumbs dynamically that look like rectangles and handled the drag and drop events so they can move inside a Canvas. Later when I press some button on the UI, I want to add some strings dynamically to each Thumb inside the Canvas. Is there a way to do it. Please help.
Xaml:
<uwpControls:LayoutTransformControl x:Name="MainLayoutControl" Grid.Row="4" Height="400" Width="600" HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid Grid.Row="4" x:Name="gridBarImagePanel" BorderBrush="Black"
BorderThickness="2">
<Image x:Name="BarCodeImage"
RenderTransformOrigin="0.5,0.5"></Image>
<Canvas x:Name="cnvBarCodeImage">
</Canvas>
</Grid>
</uwpControls:LayoutTransformControl>
Code Behind:
private void CreateUIShapes(int numberOfWindows, List<Dimensions> dimensions)
{
Thumb th = null;
for (int i = 0; i < numberOfWindows; i++)
{
th = new Thumb();
th.Name = i.ToString();
var item = dimensions[i];
th.Width = item.Width;
th.Height = item.Height;
th.Foreground = new SolidColorBrush(Windows.UI.Colors.Transparent);
th.BorderBrush = item.BorderColor;
th.BorderThickness = new Thickness(3);
th.DragDelta += (sender, e) => Th_DragDelta(sender, e, dimensions);
th.DragCompleted += (sender, e) => Th_DragCompleted(sender, e, item.IsImageRotated);
//RotateWindowsByAngle(90, th, dimensions, i);
Canvas.SetLeft(th, item.left);
Canvas.SetTop(th, item.Top);
cnvBarCodeImage.Children.Add(th);
}
}
private void BtnScan_Click(object sender, RoutedEventArgs e)
{
//How can I add some text to the Thumb controls at this point. There is no Children Property.
}
Since the Thumb isn't a ContentControl, you will have to supply it with a custom ControlTemplate to add text to it.
Here is a simple example showing how to do this in code:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Markup;
using System.Windows.Media;
namespace WpfApp4
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var thumb = new Thumb
{
Width = 100,
Height = 50,
Background = new SolidColorBrush(Colors.Red),
Foreground = new SolidColorBrush(Colors.White),
Template = GetThumbTemplate("")
};
Canvas.SetLeft(thumb, 100);
Canvas.SetTop(thumb, 100);
RootCanvas.Children.Add(thumb);
thumb.Template = GetThumbTemplate("Hello world!");
}
private ControlTemplate GetThumbTemplate(string text)
{
var template = "<ControlTemplate xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" TargetType=\"Thumb\">" +
"<Border Background=\"{TemplateBinding Background}\">" +
"<TextBlock VerticalAlignment=\"Center\" HorizontalAlignment=\"Center\" Text=\"" + text + "\" />" +
"</Border>" +
"</ControlTemplate>";
return XamlReader.Parse(template) as ControlTemplate;
}
}
}
When I create the Thumb I set the Template with GetThumbTemplate and pass in an empty string. GetThumbTemplate creates a simple ControlTemplate with a Border and a TextBlock. I then parse the XAML and return it.
After I add it to the Canvas, I update the Template of the Thumb using the GetThumbTemplate method, passing in the string I want to display.
I hope this helps!
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.
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 trying to make grid that would contain some number of buttons (1,4,8).
<Window x:Class="WpfApplication9.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
<!--<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Button Grid.Row="0" Grid.Column="0"></Button>
<Button Grid.Row="0" Grid.Column="1"></Button>
<Button Grid.Row="1" Grid.Column="0"></Button>
<Button Grid.Row="1" Grid.Column="1"></Button>
</Grid>-->
<ItemsControl ItemsSource="{Binding TheList}" x:Name="MyGrid" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Height="Auto" Grid.Row="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<StackPanel Orientation="Horizontal">
<Button Command="{Binding One}">1</Button>
<Button Command="{Binding Four}">4</Button>
<Button Command="{Binding Eight}">8</Button>
</StackPanel>
</Grid>
Code:
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace WpfApplication9
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
///
public partial class MainWindow : Window
{
public class ViewModelTest : INotifyPropertyChanged
{
private ObservableCollection<Button> theList;
public ObservableCollection<Button> TheList
{
get { return theList; }
set { theList = value; NotifyPropertyChanged("TheList"); }
}
public ViewModelTest()
{
One = new RelayCommand(() => OneButton());
Four = new RelayCommand(() => FourButton());
Eight = new RelayCommand(() => EightButton());
//TheList.Clear();
//for (int i = 0; i < 4; i++)
//{
// System.Windows.Controls.Button newBtn = new Button();
// newBtn.Content = i.ToString();
// newBtn.Name = "Button" + i.ToString();
// TheList.Add(newBtn);
//}
}
public ICommand One { get; set; }
public ICommand Four { get; set; }
public ICommand Eight { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public void OneButton()
{
TheList = new ObservableCollection<Button>();
for (int i = 0; i < 1; i++)
{
Button btn = new Button();
System.Windows.Controls.Button newBtn = new Button();
newBtn.Content = i.ToString();
newBtn.Name = "Button" + i.ToString();
TheList.Add(newBtn);
}
}
public void FourButton()
{
TheList = new ObservableCollection<Button>();
for (int i = 0; i < 4; i++)
{
Button btn = new Button();
System.Windows.Controls.Button newBtn = new Button();
newBtn.Content = i.ToString();
newBtn.Name = "Button" + i.ToString();
TheList.Add(newBtn);
}
//MyGrid.Children.Clear();
//MyGrid.ColumnDefinitions.Clear();
//MyGrid.RowDefinitions.Clear();
//MyGrid.ColumnDefinitions.Add(new ColumnDefinition());
//MyGrid.ColumnDefinitions.Add(new ColumnDefinition());
//MyGrid.RowDefinitions.Add(new RowDefinition());
//MyGrid.RowDefinitions.Add(new RowDefinition());
//for (int row =0;row<MyGrid.ColumnDefinitions.Count;row++)
//{
// for (int column=0;column<MyGrid.ColumnDefinitions.Count;column++)
// {
// System.Windows.Controls.Button newBtn = new Button();
// newBtn.Content = row.ToString() + column.ToString();
// newBtn.Name = "Button" + row.ToString()+column.ToString();
// newBtn.SetValue(Grid.ColumnProperty,column);
// newBtn.SetValue(Grid.RowProperty, row);
// MyGrid.Children.Add(newBtn);
// }
//}
}
public void EightButton()
{
TheList = new ObservableCollection<Button>();
for (int i = 0; i < 8; i++)
{
Button btn = new Button();
System.Windows.Controls.Button newBtn = new Button();
newBtn.Content = i.ToString();
newBtn.Name = "Button" + i.ToString();
TheList.Add(newBtn);
}
//MyGrid.Children.Clear();
//MyGrid.ColumnDefinitions.Clear();
//MyGrid.RowDefinitions.Clear();
//MyGrid.ColumnDefinitions.Add(new ColumnDefinition());
//MyGrid.ColumnDefinitions.Add(new ColumnDefinition());
//MyGrid.ColumnDefinitions.Add(new ColumnDefinition());
//MyGrid.ColumnDefinitions.Add(new ColumnDefinition());
//MyGrid.RowDefinitions.Add(new RowDefinition());
//MyGrid.RowDefinitions.Add(new RowDefinition());
//for (int row = 0; row < MyGrid.ColumnDefinitions.Count; row++)
//{
// for (int column = 0; column < MyGrid.ColumnDefinitions.Count; column++)
// {
// System.Windows.Controls.Button newBtn = new Button();
// newBtn.Content = row.ToString() + column.ToString();
// newBtn.Name = "Button" + row.ToString() + column.ToString();
// newBtn.SetValue(Grid.ColumnProperty, column);
// newBtn.SetValue(Grid.RowProperty, row);
// MyGrid.Children.Add(newBtn);
// }
//}
}
}
public MainWindow()
{
DataContext = new ViewModelTest();
InitializeComponent();
}
}
But I have a problem with displaying. How can I create new columns and rows in itemcontrol dynamically in this case.
Ok, where to begin....
You've got a couple problems here.
TheList is an ObservableCollection, so there is no need for using 'NotifyPropertyChanged' in the TheList property. And no need for the theList member variable. Just use the following for your property:
public ObservableCollection TheList { get; set; }
However, in your code behind, 'TheList' doesn't get initialized until one of your buttons is clicked. Your XAML, therefore is binding to a null object, so pretty much not binding to anything, no matter what you change 'TheList' to after the binding, you're still bound to null. To fix this, make sure to initialize TheList in your constructor before your InitializeComponents() call. Change your constructor to the following:
public MainWindow()
{
TheList = new ObservableCollection<Button>();
DataContext = new ViewModelTest();
InitializeComponent();
}
Also, all the places where you are setting TheList = new ObservableCollection<Button>() just changes the pointer to something else, so the Binding gets broken again. Instead of re-initializing TheList, just do TheList.Clear(); and then add your buttons to TheList using TheList.Add(newBtn);
So, it looks like you have a bunch of RowDefinitions without heights, I'm guessing these are just place holders? You could name your Grid something like x:Name="MyInternalGrid" and then when you're adding to TheList, do the same thing with adding rows to MyInternalGrid. So when call TheList.Clear(); also clear your gridRows by calling MyInternalGrid.RowDefinitions.Clear();. Then, whenever you call TheList.Add(newBtn);, add another row to your grid with MyInternalGrid.RowDefinitions.Add(new RowDefinition{Height=20});. When you do that, make sure to assign the correct row to your newBtn. Grid.SetRow(newBtn, MyInternalGrid.RowDefinitions.Count); before adding the newBtn to TheList. Here's what I mean, change your methods to mimic the refactored EightButton() method below:
public void EightButton()
{
TheList.Clear();
MyInternalGrid.RowDefinitions.Clear();
for (int i = 0; i < 8; i++)
{
Button newBtn = new Button();
newBtn.Content = i.ToString();
newBtn.Name = "Button" + i.ToString();
Grid.SetRow(newBtn, MyInternalGrid.RowDefinitions.Count);
MyInternalGrid.RowDefinitions.Add(new RowDefinition{Height=20});
TheList.Add(newBtn);
}
}
Or, you might just want to not worry about all the grid rows and put a <StackPanel> inside your <Grid> and add all the buttons to that <StackPanel> dynamically. Just clear the stack panel first, MyStackPanel.Children.Clear(); and then in the loop add the new button like this MyStackPanel.Children.Add(newBtn);
Home some of this helps.
I've got this UserControl defined in XAML and would like to set the ItemsPanelTemplate dynamically in my code behind class (not in the XAML like in the example):
<UserControl>
<ItemsControl x:Name="Items">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid> <!-- I want to add this Grid definition in code behind -->
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</UserControl>
I tried something like
this.Items.ItemsPanel.Template = new Grid();
but failed miserably. Any help?
Background:
I only know the number of grid columns and rows at runtime.
You can do as you want by creating MannualCode in code behind as:
1. Create a Method as following which will return a ItemsPanelTemplate
private ItemsPanelTemplate GetItemsPanelTemplate()
{
string xaml = #"<ItemsPanelTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
</Grid>
</ItemsPanelTemplate>";
return XamlReader.Parse(xaml) as ItemsPanelTemplate;
}
Now add this template in your Listbox ItemsPanel as:
MyListBox.ItemsPanel = GetItemsPanelTemplate();
This is working fine for me. Hope this will help.
Keep Coding....:)
You need to create an ItemsPanelTemplate and set it's VisualTree to a FrameworkElementFactory (deprecated) which creates the Grid, or use the XamlReader to parse a XAML-string which specifies the template.
This question contains usage examples of both methods (albeit for a different template property).
An easier method to manipulate the panel at runtime is outlined in this question.
In case that you still have some work to do with the elements, you should take the following (extended) code:
First we need a helper in order to get the element:
// --------------------------------------------------------------------
// This function fetches the WrapPanel from oVisual.
private WrapPanel m_FetchWrapPanel (Visual oVisual)
{
// WrapPanel to be returned
WrapPanel oWrapPanel = null;
// number of childs of oVisual
int iNumberChilds = VisualTreeHelper.GetChildrenCount (oVisual);
// and running through the childs
int i = 0;
while ( ( i < iNumberChilds ) && ( oWrapPanel == null ) )
{ // fetching visual
Visual oVisualChild =
( VisualTreeHelper.GetChild (oVisual, i) as Visual );
if ( ( oVisualChild is WrapPanel ) is true )
{ // found
oWrapPanel = ( oVisualChild as WrapPanel );
}
else
{ // checking the childs of oVisualChild
oWrapPanel = m_FetchWrapPanel (oVisualChild);
};
// checking next child
i++;
};
// returning WrapPanel
return (oWrapPanel);
}
Now we create the Panel (or something):
// --------------------------------------------------------------------
private void m_SettingTemplate ()
{
// the online doc recommends to parse the template
string xaml =
#"<ItemsPanelTemplate
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<WrapPanel ItemWidth=""150"" MaxWidth=""150""/>
</ItemsPanelTemplate>";
// assigning the template
oMyListView.ItemsPanel = ( System.Windows.Markup.XamlReader.Parse (xaml) as ItemsPanelTemplate );
// fetching the WrapPanel
WrapPanel oWrapPanel = m_WrapPanelAusVisualHolen (oMyListView);
Debug.Assert (oWrapPanel != null);
if ( oWrapPanel != null )
{ // adjusting the size of the WrapPanel to the ListView
Binding oBinding = new Binding ("ActualWidth");
oBinding.Source = oMyListView;
oWrapPanel.SetBinding (WrapPanel.MaxWidthProperty, oBinding);
};
}
Here's a XAML based program which uses ItemsPanelTemplate with a Grid:
MainWindow.xaml:
<Window x:Class="WpfTutorialStatusBarGrid.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:WpfTutorialStatusBarGrid"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<DockPanel>
<StatusBar DockPanel.Dock="Bottom">
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem Grid.Column="0">
<TextBlock Name="lblCursorPosition" />
</StatusBarItem>
<Separator Grid.Column="1"/>
<StatusBarItem Grid.Column="2">
<TextBlock Text="c:\temp\abc.txt"/>
</StatusBarItem>
<Separator Grid.Column="3"/>
<StatusBarItem Grid.Column="4">
<ProgressBar Value="50" Width="90" Height="16"/>
</StatusBarItem>
</StatusBar>
<TextBox AcceptsReturn="True" Name="txtEditor" SelectionChanged="TxtEditor_SelectionChanged"/>
</DockPanel>
</Window>
MainWindow.xaml.cs:
using System.Windows;
namespace WpfTutorialStatusBarGrid
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void TxtEditor_SelectionChanged(object sender, RoutedEventArgs e)
{
var row = txtEditor.GetLineIndexFromCharacterIndex(txtEditor.CaretIndex);
var col = txtEditor.CaretIndex - txtEditor.GetCharacterIndexFromLineIndex(row);
lblCursorPosition.Text = $"Line {row + 1}, Char {col + 1}";
}
}
}
It's a simple text editor with a status bar:
Here's the equivalent program with the code in C# instead of XAML:
MainWindow.xaml:
<Window x:Class="WpfTutorialStatusBarGridCs.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:WpfTutorialStatusBarGridCs"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
</Grid>
</Window>
MainWindow.xaml.cs:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
namespace WpfTutorialStatusBarGridCs
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var dock_panel = new DockPanel();
Content = dock_panel;
var status_bar = new StatusBar();
dock_panel.Children.Add(status_bar);
DockPanel.SetDock(status_bar, Dock.Bottom);
var items_panel_template = new ItemsPanelTemplate();
{
var grid_factory = new FrameworkElementFactory(typeof(Grid));
{
{
var col = new FrameworkElementFactory(typeof(ColumnDefinition));
col.SetValue(ColumnDefinition.WidthProperty, new GridLength(100));
grid_factory.AppendChild(col);
}
{
var col = new FrameworkElementFactory(typeof(ColumnDefinition));
col.SetValue(ColumnDefinition.WidthProperty, new GridLength(1, GridUnitType.Auto));
grid_factory.AppendChild(col);
}
{
var col = new FrameworkElementFactory(typeof(ColumnDefinition));
col.SetValue(ColumnDefinition.WidthProperty, new GridLength(1, GridUnitType.Star));
grid_factory.AppendChild(col);
}
{
var col = new FrameworkElementFactory(typeof(ColumnDefinition));
col.SetValue(ColumnDefinition.WidthProperty, new GridLength(1, GridUnitType.Auto));
grid_factory.AppendChild(col);
}
{
var col = new FrameworkElementFactory(typeof(ColumnDefinition));
col.SetValue(ColumnDefinition.WidthProperty, new GridLength(100));
grid_factory.AppendChild(col);
}
}
items_panel_template.VisualTree = grid_factory;
}
status_bar.ItemsPanel = items_panel_template;
var text_block = new TextBlock();
{
var status_bar_item = new StatusBarItem();
Grid.SetColumn(status_bar_item, 0);
status_bar_item.Content = text_block;
status_bar.Items.Add(status_bar_item);
}
{
var separator = new Separator();
Grid.SetColumn(separator, 1);
status_bar.Items.Add(separator);
}
{
var status_bar_item = new StatusBarItem();
Grid.SetColumn(status_bar_item, 2);
status_bar_item.Content = new TextBlock() { Text = "abc" };
status_bar.Items.Add(status_bar_item);
}
{
var separator = new Separator();
Grid.SetColumn(separator, 3);
status_bar.Items.Add(separator);
}
{
var status_bar_item = new StatusBarItem();
Grid.SetColumn(status_bar_item, 4);
status_bar_item.Content = new ProgressBar() { Value = 50, Width = 90, Height = 16 };
status_bar.Items.Add(status_bar_item);
}
{
var text_box = new TextBox() { AcceptsReturn = true };
text_box.SelectionChanged += (sender, e) =>
{
var row = text_box.GetLineIndexFromCharacterIndex(text_box.CaretIndex);
var col = text_box.CaretIndex - text_box.GetCharacterIndexFromLineIndex(row);
text_block.Text = $"Line {row + 1}, Char {col + 1}";
};
dock_panel.Children.Add(text_box);
}
}
}
}
The C# version is much more verbose. However, with the help of some extension methods, it can be written in a fluent style, eliminating the intermediate variables:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var text_block = new TextBlock();
Content = new DockPanel()
.AddChildren(
new StatusBar()
.SetDock(Dock.Bottom)
.SetItemsPanel(
new ItemsPanelTemplate()
.SetVisualTree(
new FrameworkElementFactory(typeof(Grid))
.AppendChildren(
new FrameworkElementFactory(typeof(ColumnDefinition))
.SetValue_(ColumnDefinition.WidthProperty, new GridLength(100)),
new FrameworkElementFactory(typeof(ColumnDefinition))
.SetValue_(ColumnDefinition.WidthProperty, new GridLength(1, GridUnitType.Auto)),
new FrameworkElementFactory(typeof(ColumnDefinition))
.SetValue_(ColumnDefinition.WidthProperty, new GridLength(1, GridUnitType.Star)),
new FrameworkElementFactory(typeof(ColumnDefinition))
.SetValue_(ColumnDefinition.WidthProperty, new GridLength(1, GridUnitType.Auto)),
new FrameworkElementFactory(typeof(ColumnDefinition))
.SetValue_(ColumnDefinition.WidthProperty, new GridLength(100)))))
.AddItems(
new StatusBarItem() { Content = text_block }.SetColumn(0),
new Separator().SetColumn(1),
new StatusBarItem() { Content = new TextBlock() { Text = "abc" } }.SetColumn(2),
new Separator().SetColumn(3),
new StatusBarItem() { Content = new ProgressBar() { Value = 50, Width = 90, Height = 16 } }.SetColumn(4)),
new TextBox() { AcceptsReturn = true }
.AddSelectionChanged(
(sender, e) =>
{
var box = sender as TextBox;
var row = box.GetLineIndexFromCharacterIndex(box.CaretIndex);
var col = box.CaretIndex - box.GetCharacterIndexFromLineIndex(row);
text_block.Text = $"Line {row + 1}, Char {col + 1}";
}));
}
}
Here are the extension methods used:
public static class Extensions
{
public static T SetDock<T>(this T element, Dock dock) where T : UIElement
{
DockPanel.SetDock(element, dock);
return element;
}
public static T SetColumn<T>(this T element, int value) where T : UIElement
{
Grid.SetColumn(element, value);
return element;
}
public static T SetValue_<T>(this T factory, DependencyProperty dp, object value) where T : FrameworkElementFactory
{
factory.SetValue(dp, value);
return factory;
}
public static T AppendChildren<T>(this T factory, params FrameworkElementFactory[] children) where T : FrameworkElementFactory
{
foreach (var child in children)
factory.AppendChild(child);
return factory;
}
public static T SetVisualTree<T>(this T template, FrameworkElementFactory factory) where T : FrameworkTemplate
{
template.VisualTree = factory;
return template;
}
public static T1 SetItemsPanel<T1,T2>(this T1 control, T2 template) where T1 : ItemsControl where T2 : ItemsPanelTemplate
{
control.ItemsPanel = template;
return control;
}
public static T AddItems<T>(this T control, params object[] items) where T : ItemsControl
{
foreach (var item in items)
control.Items.Add(item);
return control;
}
public static T AddSelectionChanged<T>(this T obj, RoutedEventHandler handler) where T : TextBoxBase
{
obj.SelectionChanged += handler;
return obj;
}
public static T1 AddChildren<T1>(this T1 panel, params UIElement[] elements) where T1 : Panel
{
foreach (var elt in elements)
panel.Children.Add(elt);
return panel;
}
}
For anyone else...
... A control based on the ItemsControl which has an Orientation property.
It uses the FrameworkElementFactory as in the previous answers:
public class OrientationItemsControl : ItemsControl
{
public static readonly DependencyProperty OrientationProperty = WrapPanel.OrientationProperty.AddOwner(typeof(OrientationItemsControl), new PropertyMetadata(Changed));
private static void Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ItemsControl itemsControl && e.NewValue is Orientation orientation)
{
var factory = new FrameworkElementFactory(typeof(StackPanel));
factory.SetValue(OrientationProperty, orientation);
itemsControl.ItemsPanel = TemplateGenerator.CreateItemsPanelTemplate(factory);
}
}
public Orientation Orientation
{
get => (Orientation)GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
}