This question already has answers here:
How can I dynamically create a list of label and textboxes in WPF?
(2 answers)
How to generate labels, buttons, Checkboxes and textboxes dynamically in WPF App
(1 answer)
create dynamically label at wpf
(1 answer)
Closed 3 years ago.
I created labels in XAML that are multiline and all lines are centered, tested and working perfectly fine. Now I want to create these labels dynamically but unfortunately I don't know how to do this.
tried setting LineStackingStrategy on MaxHeight, Fontfamily
tried creating new Textblock on fontfamily
XAML Code to reproduce in C# code:
<Label HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Name="btn_02" Height="70" Width="160" BorderBrush="#F0F2A4" BorderThickness="2" Background="Transparent" Foreground="#F0F2A4" FontFamily="Arial" FontSize="13" TextBlock.LineStackingStrategy="BlockLineHeight" TextBlock.LineHeight="17" Content="ORGEL
LADEN" TextBlock.TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,200,0,0" MouseLeftButtonDown="Btn_02_MouseLeftButtonDown"/>
C# code to create label dynamically:
Label label = new Label()
{
HorizontalAlignment = HorizontalAlignment.Center,
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Top,
VerticalContentAlignment = VerticalAlignment.Center,
Name = "orgel" + i.ToString(),
Height = 70,
Width = 160,
BorderBrush = (SolidColorBrush)(new BrushConverter().ConvertFrom("#F0F2A4")),
BorderThickness = new Thickness(2),
Background = Brushes.Transparent,
Foreground = (SolidColorBrush)(new BrushConverter().ConvertFrom("#F0F2A4")),
FontFamily = new FontFamily("Arial"),
FontSize = 13,
Content = orgel,
Margin = new Thickness((columncount * 100), (rowcount * 100), 0, 0)
};
I want to add TextBlock.LineStackingStrategy="BlockLineHeight" TextBlock.LineHeight="17" Content="ORGEL
LADEN" TextBlock.TextAlignment="Center" from XAML to this C# code.
Whenever you want to create UI elements dynamically you should think of a DataTemplate first. Use a ItemsControl to create UI elements simply by adding data to the ObservableCollection that serves as the Binding.Source of ItemsControl.ItemsSource. The DataTemplate is used to define the appearance of the items (a Labelin your case).
Data Templating Overview
ViewModel.cs
class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<string> labelContents;
public ObservableCollection<string> LabelContents
{
get => this.labelContents;
set
{
this.labelContents = value;
OnPropertyChanged();
}
}
// Constructor
public void ViewModel()
{
// Initialize the data binding source of the ListView
this.LabelContents = new ObservableCollection<string>();
}
public void CreateLabelDynamically(string labelText)
{
this.LabelContents.Add(labelText);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
When executing CreateLabelDynamically() a new label in form of a TextBlock is added to the view.
MainWindow.xaml
<Window>
<Window.DataContext>
<ViewModel />
</Window.DataContext>
<ListView x:Name="SpectrumBars"
ItemsSource="{Binding LabelContents}">
<ListView.ItemTemplate>
<DataTemplate DataType="system:string">
<!-- The DataContext of the TextBlock is the string value contained in LabelContents -->
<TextBlock Text="{Binding}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Window>
It is also recommended to use a TextBlock instead of a Label when possible:
When data binding the Label element's Content property to the String source object, you may experience poor performance.
Avoid Databinding to the Label.Content Property
Related
I need help with drawing in my combobox. I want to make a combobox of colors for picking. I found some stuff on the internet but none of them is working. So far I have this :
private void MyComb_DrawItem(object sender, DrawItemEventArgs e)
{
Graphics g = e.Graphics;
System.Drawing.Rectangle rect = e.Bounds;
ColorConverter converter = new ColorConverter();
if (e.Index >= 0)
{
string n = ((ComboBox)sender).Items[e.Index].ToString();
System.Drawing.Color c = (System.Drawing.Color)converter.ConvertFromString(n);
SolidBrush b = new SolidBrush(c);
g.FillRectangle(b, rect.X + 110, rect.Y + 5,
rect.Width - 10, rect.Height - 10);
}
}
This is my drawItem method
<Grid>
<ComboBox x:Name="MyComb" HorizontalAlignment="Left" Margin="66,81,0,0" VerticalAlignment="Top" Width="120" />
</Grid>
This is definition of combobox
Type colorType = typeof(System.Drawing.Color);
PropertyInfo[] propInfoList = colorType.GetProperties(BindingFlags.Static |
BindingFlags.DeclaredOnly | BindingFlags.Public);
foreach (PropertyInfo c in propInfoList)
{
MyComb.Items.Add(c.Name);
}
And here I am filling combobox with colors names and then I want to fill to combox with real colors according to the colors names.. But my draw item method is never called. I tried to create some DrawItem handler but, my combobox have no such thing... Then I read something about setting a DrawMode property of combobox, but my combobox doesn't that kind of property at all...
I am using net framework v.4.6.1
Can please anyone tell me, what am I missing ?
Thank you very much
The biggest problem you're having is that you're trying to use code examples that were written for the Winforms API, even though you are using the WPF API. For future reference, you really need to be more careful about identifying the context of tutorials and other resources you find online, to make sure they actually apply to your scenario.
As it happens, we have a number of related questions on Stack Overflow already:
WPF ComboBox as System.Windows.Media.Colors>
WPF - Bind ComboBox Item Foreground to Its Value
Very simple color picker made of combobox
These are all potentially useful to you, but are all based on the answer to this question:
How can I list colors in WPF with XAML?
Which was originally about just displaying the names of colors, and so took a short-cut, using the <ObjectDataProvider/> element in XAML. This led to the need to use a converter in the other questions, to convert either from a string value or a PropertyInfo instance to the appropriate color or brush.
In fact, if your code is already written to use some type of MVVM approach, and especially since you've already written code-behind to retrieve the color values from the Colors type, or at least tried to (one of the problems in your code is that you are using the Winforms Color type instead of the WPF Colors type…again, in Winforms that works fine, but the WPF API follows the Code Analysis/FxCop rules more closely, and the named colors are in the Colors type), it makes sense to just stick with that and provide a direct view model data structure to which you can bind.
In this approach, rather than providing a procedural implementation of the item drawing, you provide in XAML a declarative implementation describing what each item should look like.
Here is an example…
First, some simple view model data structures:
// Very simple container class
class ColorViewModel
{
public Brush Brush { get; }
public string Name { get; }
public ColorViewModel(Brush brush, string name)
{
Brush = brush;
Name = name;
}
}
// Main view model, acts as the data context for the window
class MainViewModel : INotifyPropertyChanged
{
public IReadOnlyList<ColorViewModel> Colors { get; }
private Brush _selectedColor;
public Brush SelectedColor
{
get { return _selectedColor; }
set { _UpdateField(ref _selectedColor, value); }
}
public MainViewModel()
{
Colors = typeof(Colors).GetProperties(BindingFlags.Static | BindingFlags.Public)
.Select(p => new ColorViewModel(new SolidColorBrush((Color)p.GetValue(null)), p.Name))
.ToList().AsReadOnly();
}
public event PropertyChangedEventHandler PropertyChanged;
private void _UpdateField<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(field, newValue))
{
field = newValue;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
With those in hand, the XAML is straight-forward:
<Window x:Class="TestSO47850587ColorComboBox.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:l="clr-namespace:TestSO47850587ColorComboBox"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<l:MainViewModel/>
</Window.DataContext>
<StackPanel>
<ComboBox Width="100" ItemsSource="{Binding Colors}"
HorizontalAlignment="Left" Grid.IsSharedSizeScope="True"
SelectedValuePath="Brush" SelectedValue="{Binding SelectedColor}">
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type l:ColorViewModel}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="ComboBoxItem"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" Background="{Binding Brush}" HorizontalAlignment="Stretch"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Rectangle HorizontalAlignment="Stretch" Height="24" Fill="{Binding SelectedColor}"/>
</StackPanel>
</Window>
The above displays the color names, using the actual color as the background. If all you really want is a rectangle, then you can replace the <TextBlock/> element with a <Rectangle/> element, binding to its Fill property. Naturally, you can achieve other visual effects, such as a rectangle with a margin. It's just a matter of configuring your data template according to your need.
The main point here is that you should embrace the data binding approach that defines good WPF programming, and that you should definitely not mistake Winforms code examples for WPF. :)
I'm experimenting with code-behind created WPF masks as prototype for a WPF mask designer.
In my ViewModel i have a DataTable and a DataView (which is simply the DefaultView of the DataTable).
In my DataTable i got two columns ("vorname" and "nachname") and four rows.
In my WPF mask i want to have a DataGrid and two TextBoxes, which are binded to the SelectedItem of the DataGrid and the columns (either "vorname" or "nachname").
When i select an item in the DatGrid at runtime, the data from that item shall be showed in the TextBoxes.
First i tried to define the DataGrid in the XAML file and generate the TextBoxes an their bindings in code.
Here it works fine.
I select an item in the DataGrid and the data of the item is showed in the TextBoxes.
But when i generate the grid in code, it doesn't work anymore.
Is there some sort of NotifyOnSelectedIndexChanged, that i'm missing?
Any help will be appreciated.
This is the XAML:
<Window x:Class="DesignerTest.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestWindow"
Height="400"
Width="600">
<DockPanel x:Name="mainpanel">
<!--<DataGrid x:Name="datagrid"
DockPanel.Dock="Top"
Height="120" />-->
<WrapPanel x:Name="wrappanel">
<!--<TextBox x:Name="vornameSelected" Width="150" Margin="5" Text="{Binding SelectedItem.vorname, ElementName=datagrid}" IsEnabled="False" />
<TextBox x:Name="nachnameSelected" Width="150" Margin="5" Text="{Binding SelectedItem.nachname, ElementName=datagrid}" IsEnabled="False" />-->
<!--<TextBox x:Name="vornameSelected" Width="150" Margin="5" IsEnabled="False" />
<TextBox x:Name="nachnameSelected" Width="150" Margin="5" IsEnabled="False" />-->
</WrapPanel>
</DockPanel>
</Window>
And this is the code for creating and binding:
// The ViewModel und the DataTable are created.
_vm = new SerializingTestViewModel();
_vm.CreateDataTable();
this.DataContext = _vm.DataTable;
// The DataGrid and it's Binding are created.
DataGrid datagrid = new DataGrid();
datagrid.Name = "datagrid";
DockPanel.SetDock(datagrid, Dock.Top);
datagrid.Height = 120;
datagrid.ItemsSource = _vm.DataSource;
mainpanel.Children.Add(datagrid);
// The Textboxes and the Bindings are created.
TextBox vornameSelected = new TextBox();
vornameSelected.Name = "vornameSelected";
vornameSelected.Width = 150;
Thickness margin = new Thickness(5);
vornameSelected.SetValue(TextBox.MarginProperty, margin);
vornameSelected.IsEnabled = false;
Binding selectedItemBinding = new Binding();
selectedItemBinding.ElementName = "datagrid";
selectedItemBinding.Path = new PropertyPath("SelectedItem.vorname");
vornameSelected.SetBinding(TextBox.TextProperty, selectedItemBinding);
wrappanel.Children.Add(vornameSelected);
TextBox nachnameSelected = new TextBox();
nachnameSelected.Name = "nachnameSelected";
nachnameSelected.Width = 150;
margin = new Thickness(5);
nachnameSelected.SetValue(TextBox.MarginProperty, margin);
nachnameSelected.IsEnabled = false;
selectedItemBinding = new Binding();
selectedItemBinding.ElementName = "datagrid";
selectedItemBinding.Path = new PropertyPath("SelectedItem.nachname");
nachnameSelected.SetBinding(TextBox.TextProperty, selectedItemBinding);
wrappanel.Children.Add(nachnameSelected);
Try setting your binding source using the Source property instead of ElementName
//selectedItemBinding.ElementName = "datagrid"
selectedItemBinding.Source = datagrid;
The problem might be that the ElementName lookup for items is not working as expected because items are added dynamically at runtime via code behind.
I have a part of text which some of the words are formatted.
These text are listed in a ListBox. When user clicks ListBoxitem, I want to collect that selectedItem and take user to the other place. My problem is that I cant bind TextBlock with another instance of TextBlock. And that TextBlock has many inlines, which I want to show.
I have been trying this solution:
<ListBox Width="800" Name="foundedTextBlocksListBox" SelectionChanged="foundedTextBlocksListBox_SelectionChanged" Background="Transparent" ItemsSource="{Binding}" Grid.Row="2" Visibility="Visible" Height="Auto" HorizontalAlignment="Center">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel VerticalAlignment="Center" Orientation="Vertical">
<TextBlock x:Name="foundedTextBlocks" DataContext="{Binding Textblock}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
After Binding to DataContext like this:
ObservableCollection<FoundedTextBlock> listOfFoundedTextBlockResults = new ObservableCollection<FoundedTextBlock>();
TextBlock textblock = new TextBlock();
while (blockString.IndexOf("<b>") != -1)
{
int startOfWord = blockString.IndexOf("<b>");
int endOfWord = blockString.IndexOf("</b>");
string text = blockString.Substring(0, startOfWord);
textblock.Inlines.Add(text);
string boldedWord = blockString.Substring(startOfWord + 3, endOfWord - startOfWord - 3);
textblock.Inlines.Add(new Run() { Text = boldedWord, FontWeight = FontWeights.Bold });
blockString = blockString.Substring(endOfWord + 4);
textblock.Inlines.Add(blockString);
}
textblock.Tag = dbInfo;
listOfFoundedTextBlockResults.Add(new FoundedTextBlock() { Textblock = textblock });
}
foundedTextBlocksListBox.DataContext = listOfFoundedTextBlockResults;
I can't see any ListBoxItems in ListBox. Is my Binding wrong or is this possible at all?
I managed before to get TextBlock.Text property to show but not the Inlines where are bolded text or any other Inlines after my first inline addition to TextBlock.
How I can solve this annoiyng problem? In short, I need to display many TextBlocks with formatted text...
FoundedTextBlock class has TextBlock textblock {get;set;} property
I'm saving to Tag property my class instance, so I could collect the information I need when SelectedValueChanged event occurs.
Maybe you should use a ContentPresenter instead of TextBlock in your XAML
Replace
<TextBlock x:Name="foundedTextBlocks" DataContext="{Binding Textblock}"></TextBlock>
with
<ContentPresenter Content="{Binding Textblock}" />
Please try it out ... the rest of your code is missing to provide a better answer.
This question already has answers here:
Assigning events in object initializer
(3 answers)
Closed 9 years ago.
I am creating a TextBox in code-behind.
TextBox textBox = new TextBox();
I also have a function:
private void TextBox_Focus(object sender, RoutedEventArgs e)
{
// does something
}
I want to bind TextBox_Focus to TextBox.GotFocus.
Rather than setting each property individually like so
TextBox textBox = new TextBox();
textBox.Width = 100;
textBox.Height = 25;
textBox.Background = Brushes.White;
textBox.Foreground = Brushes.Blue;
textBox.GotFocus += TextBox_Focus;
I prefer using braces (curly brackets) {}:
TextBox textBox = new TextBox()
{
Width = 100,
Height = 25,
Background = Brushes.White,
Foreground = Brushes.Blue
};
However, when I use the braces method, I am unable to bind to events.
I have tried doing the following, but to no avail...
TextBox textBox = new TextBox()
{
Width = 100,
Height = 25,
Background = Brushes.White,
Foreground = Brushes.Blue,
this.GotFocus += TextBox_Focus
};
Question:
Is there a way to event bind using the braces ({}) method?
Update:
The element is being created dynamically, so I cannot use XAML.
No. Object initializers only work to set properties or fields. You're trying to subscribe to an event, which isn't supported in Object initializer syntax.
As other commenters are saying, XAML is the best way to initialize WPF controls.
Apparently Mono though supports what you're asking for. See: Initializing events with initializer syntax
Why not use Xaml, you'll find it is quite flexible.
And also kinda WPF's thing.
<TextBox x:Name="textBox"
Width="100"
Height="25"
Background="White"
Foreground="Blue"
GotFocus="TextBox_Focus" />
As per your comment, you can do what you wish like so:
<ListBox ItemsSource="{Binding MyCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding }"
Width="100"
Height="25"
Background="White"
Foreground="Blue"
GotFocus="TextBox_Focus" />
</DataTemplate>
</ListBox.ItemTemplate>
If you make your Collection an ObservableCollection<T> when you add an item to the collection it will update your list box for you.
Try
EventManager.RegisterClassHandler(typeof(TextBox),TextBox.GotKeyboardFocusEvent, new RoutedEventHandler(yourMethod());
I have a TextBlock control in my view, its Width depends on the Text property.
I'm looking for some way to bind the TextBlocks Width to a property in my model,which will work as follows:
The setting of the Width must be done automatically based on Text
In my button click I would like to retrieve the Width
I've tried the code below, but it keeps the Width as 0 if I don't explicitly set it in the constructor of the view model.Tried Mode=OneWayToSource and Mode=OneWay but it made no difference, any suggestions?
View:
<Grid>
<TextBlock Text="Some text" Width="{Binding TextWidth,Mode=OneWayToSource}" />
<Button Content="Show Width" Height="30" Width="90" Command="{Binding ShowTextWidth}" />
</Grid>
View model:
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private DelegateCommand<object> showTextWidth;
public DelegateCommand<object> ShowTextWidth
{
get { return showTextWidth; }
set { showTextWidth = value; }
}
private double textWidth;
public double TextWidth
{
get { return textWidth; }
set
{
textWidth = value;
OnPropertyChanged("TextWidth");
}
}
public ViewModel()
{
//If I explicitly specify the width it works:
//TextWidth = 100;
ShowTextWidth = new DelegateCommand<object>(ShowWidth);
}
private void ShowWidth(object parameter)
{
MessageBox.Show(TextWidth.ToString());
}
}
Ended up creating an attached behavior by Maleak which was inspired by Kent Boogaarts Pushing read-only GUI properties back into ViewModel, can't believe it's so complicated to push the value of ActualWidth into the view model!
Width is a DependencyProperty on TextBlock. In this case it's a Target for Binding and TextWidth is your source for Binding. OneWayToSource seems like the way to go, you are setting TextWidth to 100 in the ViewModel which does not propogate to Width on TextBlock because it's OneWayToSource yes correct, Width (Target) is then setting TextWidth (Source) to Double.NaN because of OneWayToSource and that's why you're seeing 0...
ActualWidth should work like sa_ddam213 said but also consider that your TextBlock doesn't grow in Width (ActualWidth) when the Text changes because it is spanning the total width of your Grid layout. Either put it in a ColumnDefinition with Width set to Auto or make your HorizontalAlignment Left to see the ActualWidth change when the Text changes.
I have made some changes to your source code. Consider using CommandParameter on your button? Check out the link...
WpfApplication10.zip
you dont really need to set the Width if you choose a layout panel wich can handle this for you.
eg. a columndefinition with width=auto grows with your text
To set the Width depending on its containing Text you could bind the Width-Property to the Text-Property and use a converter like so:
<TextBlock Width="{Binding RelativeSource={RelativeSource Self}, Path=Text, Converter={StaticResource TextToWidth}}"
Your converter could look like:
TextBlock tb = new TextBlock();
tb.Text = value.ToString();
tb.Measure(new Size(int.MaxValue, 20)); //20 if there shouldnt be a linebreak
return tb.DesiredSize.Width;