I am developing WPF application. In which I am adding CheckBoxes to a ListBox in following way.
foreach (User ls in lst)
{
AddContacts(ls, lstContactList);
}
private void AddContacts(User UserData, ListBox lstbox)
{
try
{
var txtMsgConversation = new CheckBox()
{
Padding = new Thickness(1),
IsEnabled = true,
//IsReadOnly = true,
Background = Brushes.Transparent,
Foreground = Brushes.White,
Width = 180,
Height = 30,
VerticalAlignment = VerticalAlignment.Top,
VerticalContentAlignment = VerticalAlignment.Top,
Content = UserData.Name, //+ "\n" + UserData.ContactNo,
Margin = new Thickness(10, 10, 10, 10)
};
var SpConversation = new StackPanel() { Orientation = Orientation.Horizontal };
SpConversation.Children.Add(txtMsgConversation);
var item = new ListBoxItem()
{
Content = SpConversation,
Uid = UserData.Id.ToString(CultureInfo.InvariantCulture),
Background = Brushes.Black,
Foreground = Brushes.White,
BorderThickness = new Thickness(1),
BorderBrush = Brushes.Gray
};
item.Tag = UserData;
lstbox.Items.Add(item);
}
catch (Exception ex)
{
//Need to log Exception
}
}
Now I need to get the checked items from ListBox. How do I proceed here, I tried below code, which returning null,
CheckBox chkBox = lstContactList.SelectedItem as CheckBox;
Thoughts?
The way of creating dynamic multiple items in a listbox is not in codebehind, but to create a template for the items, and then bind it to a list of items.
Example
Say I have a bunch of passages List<Passage> Passages { get; set; }:
public class Passage
{
public string Name { get; set; }
public bool IsSelected { get; set; }
}
In my xaml I create a template and bind to it
<ListBox ItemsSource="{Binding Passages}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" />
<TextBlock Text="{Binding Path=Name, StringFormat=Passage: {0}}"
Foreground="Blue" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The result looks like this for my four passages "Alpha", "Beta", "Gamma" and "I-25":
Then if I want the selected item, such as the recently checked Beta above, I just enumerate my List for the selected one(s).
var selecteds = Passages.Where(ps => ps.IsSelected == true);
Need to list different types objects in one ListBox? Say from binding to a composite collection or an ObservableCollection<T>?
See my answers here:
Composite Collection
ObservableCollection
Related
Suppose I have a list which contains some elements and texts:
public MainWindow()
{
InitializeComponent();
TextBlock t = new TextBlock();
t.Text = "Hello World!";
Ellipse e = new Ellipse();
e.Width = 100;
e.Height = 100;
e.Fill = new SolidColorBrush(Colors.Yellow);
Rectangle r = new Rectangle();
r.Width = 70;
r.Height = 40;
r.Fill = new SolidColorBrush(Colors.Blue);
List<UIElementItem> items = new List<UIElementItem>();
items.Add(new UIElementItem() { uiElement = t, Text = "This is a TextBlock: " });
items.Add(new UIElementItem() { uiElement = e, Text = "This is an Ellipse: " });
items.Add(new UIElementItem() { uiElement = r, Text = "This is a Rectangle: " });
lbListBox.ItemsSource = items;
}
}
public class UIElementItem
{
public UIElement uiElement { get; set; }
public string Text { get; set; }
}
This is the ListBox in XAML:
<ListBox Name="lbListBox" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,2" Orientation="Horizontal">
<TextBlock Text="{Binding Text}"/>
<??? = "{Binding uiElement}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I want to make the ListBox show the texts and the elements.
The TextBlock Text="{Binding Text}" will show the text but how to show the element? How to bind?
You could use a ContentPresenter
<ContentPresenter Content="{Binding uiElement}" />
This would show the UI Elements. The complete ItemTemplate would look like
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,2" Orientation="Horizontal">
<TextBlock Text="{Binding Text}"/>
<ContentPresenter Content="{Binding uiElement}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
I have tried to make a page in my app where all controls are generated dynamically via C# code behind. I am using a Nuget Packages, DLToolkit, flowlist to create a flow list.
I have already used this package in my project before using Xaml, and it fully works.
However when I try to create a datatemplate in code behind, it just displays a blank control, however when hovering above this control, you can see there's actually items in it.
My question is: How can I make a datatemplate with databindings in code behind?
Here is an example and works in Xaml:
<flv:FlowListView x:Name="flvList" BackgroundColor="White" FlowColumnCount="3" FlowItemsSource="{Binding LstItemSource}" HasUnevenRows="True">
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate>
<StackLayout BackgroundColor="White" Padding="2" HorizontalOptions="FillAndExpand">
<Frame Margin="20" Padding="0" HeightRequest="175" OutlineColor="Gray" BackgroundColor="White" CornerRadius="10" HasShadow="True" IsClippedToBounds="True">
<Frame.Content>
<AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" >
<Image Aspect="AspectFill" AbsoluteLayout.LayoutBounds="1,1,1,1" AbsoluteLayout.LayoutFlags="All" Source="{Binding BgImage}" />
<BoxView Color="Black" Opacity=".5" AbsoluteLayout.LayoutBounds="1,1,1,1" AbsoluteLayout.LayoutFlags="All"/>
<StackLayout Margin="20" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
<Label Text="{Binding SubTitle}" FontSize="Medium" TextColor="#66FFFFFF"/>
<Label Text="{ Binding Title}" FontSize="Large" TextColor="White" />
</StackLayout>
</AbsoluteLayout>
</Frame.Content>
</Frame>
</StackLayout>
</DataTemplate>
</flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>
However, in this project the controls are generated, so there is no Xaml code involved.
This is an example of the code that I've tried in code behind, but doesn't work:
#region Datatemplate
var dataTemplate = new DataTemplate(() =>
{
var StackLayout = new StackLayout { BackgroundColor = Color.Pink, Padding = 2, HorizontalOptions = LayoutOptions.FillAndExpand };
#region children/content for frame
AbsoluteLayout absoluteLayout = new AbsoluteLayout { HorizontalOptions = LayoutOptions.FillAndExpand, VerticalOptions = LayoutOptions.FillAndExpand };
#region content for AbsoluteLayout
var imgBg = new Image();
AbsoluteLayout.SetLayoutBounds(imgBg , new Rectangle(1, 1, 1, 1));
AbsoluteLayout.SetLayoutFlags(imgBg , AbsoluteLayoutFlags.All);
imgBg .SetBinding(Image.SourceProperty, "BgImage");
absoluteLayout.Children.Add(imgBg );
var overlayBox = new BoxView { Color = Color.Black, Opacity = 0.5 };
AbsoluteLayout.SetLayoutBounds(overlayBox, new Rectangle(1, 1, 1, 1));
AbsoluteLayout.SetLayoutFlags(overlayBox, AbsoluteLayoutFlags.All);
absoluteLayout.Children.Add(overlayBox);
#region InnerStackpanel
StackLayout innerStackVoorAbsoluteLayout = new StackLayout { Margin = new Thickness(20), VerticalOptions = LayoutOptions.CenterAndExpand, HorizontalOptions = LayoutOptions.CenterAndExpand };
var lblTitel = new Label { FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)), TextColor = Color.White };
var lblSubTitel = new Label { FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)), TextColor = Color.White };
//Bindings
lblTitel.SetBinding(Label.TextProperty, "Title");
lblSubTitel.SetBinding(Label.TextProperty, "SubTitle");
innerStackVoorAbsoluteLayout.Children.Add(lblSubTitel);
innerStackVoorAbsoluteLayout.Children.Add(lblTitel);
absoluteLayout.Children.Add(innerStackVoorAbsoluteLayout);
#endregion
#endregion
#endregion
Frame frame = new Frame();
frame.Content = absoluteLayout;
StackLayout.Children.Add(frame);
return StackLayout;
});
#endregion
FlowListView lstRelatieLijst = new FlowListView();
lstRelatieLijst.ItemsSource = lstRelatieItems;
lstRelatieLijst.FlowColumnTemplate = dataTemplate;
lstRelatieLijst.BackgroundColor = Color.LightGoldenrodYellow;
lstRelatieLijst.FlowColumnCount = 1;
lstRelatieLijst.HasUnevenRows = true;
#endregion
Can someone give me some advice how I can become something similar like the upper Xaml in code behind, please?
I already tried the following sources but unfortunately I doesn't work as expected. I hoped to see the same result or something similar like the XAML code. But after following their info,the FLowListView appears to be empty:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/templates/data-templates/creating
https://www.codeproject.com/Questions/516614/createplusdatatemplatepluscodeplusbehind
You should use
flowList.SetBinding(FlowListView.FlowItemsSourceProperty, "List"); instead of ItemsSource
Here is the working sample....
using DLToolkit.Forms.Controls;
using System;
using Xamarin.Forms;
namespace FlowListTest
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
LoadUI();
BindingContext = new BContext();
}
private void LoadUI()
{
var dataTemplate = new DataTemplate(() =>
{
var image = new Image();
image.SetBinding(Image.SourceProperty, "BgImage");
var titleLabel = new Label
{
FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)),
TextColor = Color.White,
};
titleLabel.SetBinding(Label.TextProperty, "Title");
var subTitleLabel = new Label
{
FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)),
TextColor = Color.White,
};
subTitleLabel.SetBinding(Label.TextProperty, "Subtitle");
return new StackLayout
{
BackgroundColor = Color.Pink,
Padding = 2,
HorizontalOptions = LayoutOptions.FillAndExpand,
Children = {
new Frame {
Content = new AbsoluteLayout {
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
Children = {
image,
new StackLayout {
Margin = new Thickness(20),
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
Children = {
titleLabel,
subTitleLabel
}
}
}
}
}
}
};
});
var flowList = new FlowListView();
flowList.SetBinding(FlowListView.FlowItemsSourceProperty, "List");
flowList.FlowColumnTemplate = dataTemplate;
flowList.BackgroundColor = Color.LightGoldenrodYellow;
flowList.FlowColumnCount = 1;
flowList.HasUnevenRows = true;
var button = new Button { Text = "Add" };
button.Clicked += Button_Clicked
;
Content = new StackLayout
{
Children = {
button,
flowList
}
};
}
private void Button_Clicked(object sender, EventArgs e)
{
(BindingContext as BContext).Add();
}
}
public class Foo
{
public string BgImage { get; set; }
public string Title { get; set; }
public string Subtitle { get; set; }
}
public class BContext
{
public FlowObservableCollection<Foo> List { get; set; }
public BContext()
{
List = new FlowObservableCollection<Foo>
{
new Foo {
BgImage = "http://via.placeholder.com/350x150",
Title = "Title",
Subtitle = "SubTitle"
},
new Foo {
BgImage = "http://via.placeholder.com/350x150",
Title = "Title1",
Subtitle = "SubTitle1"
}
};
}
public void Add()
{
List.Add(new Foo
{
BgImage = "http://via.placeholder.com/350x150",
Title = "Title" + List.Count,
Subtitle = "SubTitle" + List.Count
});
}
}
}
I am trying to change the font colour of a specific item of my listview in code and I am not sure how to approach it. This is what I have so far:
lv_options.ItemsSource = new Options[] {
new Options { Text = "Delete" },
new Options { Text = "Rename"} ,
new Options { Text = "Order: Move up" },
new Options { Text = "Order: Move down"}
};
if (act.item.SectionPosition >= act.lst_sections.Count() || act.item.SectionPosition <= 1)
{
foreach (Options op in lv_options.ItemsSource)
{
if(op.Text.Equals("Order: Move up"))
{
lv_options.HeaderTemplate.SetValue....???
}
}
}
This is my listview
<ListView x:Name="lv_options">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" Padding="10, 15, 0, 15">
<Label Font = "20" Text="{Binding Text}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
You will need to identify which control you want to change.
Then find the control to add the attribute.
Example:
Label label = (Label)ListViewID.FindControl("LabelID");
label.Attributes.Add("color", "red");
int index = 1;
ListViewItem item = this.lv_options.ItemContainerGenerator.ContainerFromIndex(index) as ListViewItem;
item.Foreground = Brushes.Green;
I have two TextBlock’s that I am positioning consecutively on a Canvas. The first case works fine:
TextBlock text1 = new TextBlock();
text1.Text = "Not ";
text1.FontSize = 18;
Canvas.SetTop(text1, 20);
Canvas.SetLeft(text1, 20);
canvas.Children.Add(text1);
TextBlock text2 = new TextBlock();
text2.Text = "bad!";
text2.FontSize = 18;
Canvas.SetTop(text2, 20);
canvas.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(delegate(Object state)
{
Canvas.SetLeft(text2, 20 + text1.ActualWidth);
return null;
}
), null);
canvas.Children.Add(text2);
Result:
However, the second case, which does not use BeginInvoke(), fails:
TextBlock text1 = new TextBlock();
text1.Text = "Not ";
text1.FontSize = 18;
Canvas.SetTop(text1, 20);
Canvas.SetLeft(text1, 20);
canvas.Children.Add(text1);
TextBlock text2 = new TextBlock();
text2.Text = "bad!";
text2.FontSize = 18;
Canvas.SetTop(text2, 20);
Canvas.SetLeft(text2, 20 + text1.ActualWidth); // ActualWidth is zero.
canvas.Children.Add(text2);
Result:
Now, I know that in the second case, the WPF rendering has not happened yet. My question is simply this: What is the preferred pattern to use in such a case where I need to know the actual coordinate values for UI controls which are only available after rendering has taken place?
(e.g. Is the approach, where BeginInvoke() is used, a good solution? Should the entire code be enclosed in a giant BeginInvoke()?)
To answer your question:
Dispatcher.BeginInvoke() queues the operation in the Dispatcher's "pending jobs" queue. This allows it to be able to process the addition of the first UI element, and run the Layout and Render passes before continuing to execute your code.
Therefore, when your code is run, the size of the first TextBlock has already been calculated, and you can get it.
Again, I don't know what you're attempting to do, but creating UI elements in code is usually a sign of a poor design. WPF is not winforms and the WPF ways are completely different from the horrible hacks required to do anything in winforms.
Edit:
This is my approach using a WrapPanel and some RenderTransform:
<Window x:Class="MiscSamples.MovingWords"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MovingWords" Height="300" Width="300">
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Thumb DragDelta="Thumb_DragDelta" Margin="2">
<Thumb.Template>
<ControlTemplate>
<TextBlock Text="{Binding Text}"
FontSize="{Binding FontSize}"
Foreground="{Binding Color}"/>
</ControlTemplate>
</Thumb.Template>
<Thumb.RenderTransform>
<TranslateTransform X="{Binding OffsetX}" Y="{Binding OffsetY}"/>
</Thumb.RenderTransform>
</Thumb>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
Code Behind:
public partial class MovingWords : Window
{
public ObservableCollection<MovingWordModel> Words { get; set; }
public MovingWords()
{
InitializeComponent();
Words = new ObservableCollection<MovingWordModel>
{
new MovingWordModel() {Color = "Black", FontSize = 18, Text = "Hello!!"},
new MovingWordModel() {Color = "Black", FontSize = 18, Text = "This"},
new MovingWordModel() {Color = "Black", FontSize = 18, Text = "is"},
new MovingWordModel() {Color = "Black", FontSize = 18, Text = "the"},
new MovingWordModel() {Color = "Black", FontSize = 18, Text = "Power"},
new MovingWordModel() {Color = "Black", FontSize = 18, Text = "of"},
new MovingWordModel() {Color = "Blue", FontSize = 18, Text = "WPF"},
};
DataContext = Words;
}
private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
var thumb = sender as Thumb;
if (thumb == null)
return;
var word = thumb.DataContext as MovingWordModel;
if (word == null)
return;
word.OffsetX += e.HorizontalChange;
word.OffsetY += e.VerticalChange;
}
}
Data Model:
public class MovingWordModel:PropertyChangedBase
{
public string Text { get; set; }
public int FontSize { get; set; }
public string Color { get; set; }
private double _offsetX;
public Double OffsetX
{
get { return _offsetX; }
set
{
_offsetX = value;
OnPropertyChanged("OffsetX");
}
}
private double _offsetY;
public double OffsetY
{
get { return _offsetY; }
set
{
_offsetY = value;
OnPropertyChanged("OffsetY");
}
}
}
PropertyChangedBase:
public class PropertyChangedBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
You can click and drag the words to move them around.
Notice that the values from the dragging will be stored in the OffsetX and OffsetY properties. The only problem with this approach is that you somewhat lose the Resolution Independence, because the offset values will actually move the words from their default position (which is determined by the WrapPanel, therefore they're subject to change depending on the size of the WrapPanel itself).
i need to show a list of item in a ListBox from the bottom in WP7.
So in case i have some items that the height sum of them is < of ListBox Height i need to have a blank item at the top with the difference of the Height.
I have to do this because i set the ItemSource of Listbox, so i cannot know what is the right height of all items before load them.
In Item_loaded event of every Item i save the height and at the last i need to set the Height of the First.
<ListBox x:Name="ConvListBox" Margin="0,0,-12,0" >
<ListBox.ItemTemplate >
<DataTemplate >
<Grid>
<StackPanel Name="BaloonMessage" Margin="3,0,0,0" Loaded="Baloon_Loaded" Tag="{Binding IsSentMsg}" >
<TextBlock Name="SMSText" Text="{Binding SMSText}" Margin="7,3,8,35" TextWrapping="Wrap" Height="Auto" Width="Auto" FontSize="22" Foreground="White"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I set the ItemsSource and add a blank item at top, and a blank at bottom:
ObservableCollection<ClassMessaggio> messaggi =
new ConversazioneViewModel(MessaggioConversazione).Conversazione;
ClassMessaggio FirstLineScrollMessage = new ClassMessaggio();
FirstLineScrollMessage.IsSentMsg = "3";
messaggi.Insert(0, FirstLineScrollMessage);
ClassMessaggio LastLineScrollMessage = new ClassMessaggio();
LastLineScrollMessage.IsSentMsg = "2";
messaggi.Insert(messaggi.Count, LastLineScrollMessage);
this.ConvListBox.ItemsSource = messaggi;
And at Item_Loaded i'm trying this:
var Panel = (StackPanel) sender;
if (Panel != null)
{
Grid grid = (Grid)Panel.Parent;
Border baloon = (Border)Panel.FindName("Baloon");
baloon.Width = grid.Width - 100;
if (Panel.Tag.ToString() == "3")
{
TotalBaloonsHeight = 0;
baloon.Background = grid.Background;
baloon.Name = "FirstScrollBaloon";
}
else if (Panel.Tag.ToString() == "2")
{
baloon.Height = 2;
Panel.Height = 2;
grid.Height = 2;
Border FirstBaloon = (Border)ConvListBox.FindName("FirstScrollBaloon");
if (FirstBaloon != null)
{
FirstBaloon.Height = ConvListBox.Height - TotalBaloonsHeight;
}
}
else
{
TotalBaloonsHeight = TotalBaloonsHeight + baloon.Height;
}
}
My problem is that this line return me always null :(
Border FirstBaloon = (Border)ConvListBox.FindName("FirstScrollBaloon");
I hope is clear, sorry for my english.
EDIT::
Ok this should work:
var Baloons = LayoutRoot.GetVisualDescendants().OfType<Border>();
foreach (var FirstBaloon in Baloons)
{
if (FirstBaloon != null)
{
if (FirstBaloon.Name == "FirstScrollBaloon")
{
FirstBaloon.Height = ConvListBox.ActualHeight - TotalBaloonsHeight;
break;
}
}
}
You can get to the first ListBoxItem using this code:
ListBoxItem item0 = ConvListBox.ItemContainerGenerator.ContainerFromIndex(0) as ListBoxItem;
From there you can modify it's Height, etc.
Thanks,
Stefan Wick - Microsoft Silverlight