I'm using a ListBox in combination with a ObservableCollection. The content is set via a TemplateSelector (TextBlock or Label). The text has to be formatted (f.e. with Run-Tags in Code-behind), but i can't access the Items. Is there a solution to get the elements?
I've tried the usage of OfType<>, but this works only on Panels. I searched for an children-attribute but, there isn't one for ListBoxes. Setting the Name-Property via binding is not possible for UId and Name.
An IEnumerator for the LogicalChildren doesn't work and iterate over the whole content everytime a new element is added, is not so optimal. Here a minimal example.
<Window.Resources>
<DataTemplate x:Key="TextBlockTemplate">
<StackPanel>
<TextBlock />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="LabelTemplate">
<StackPanel>
<Label/>
</StackPanel>
</DataTemplate>
<local:myTemplateSelector x:Key="myTemplateSelector" x:Name="myTemplateSelector" TextBlockTemplate="{StaticResource TextBlockTemplate}" LabelTemplate="{StaticResource LabelTemplate}"/>
</Window.Resources>
<Grid Margin="0">
<ListBox Name="mylist" Grid.Row="3"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding _listEntries}"
ItemTemplateSelector="{StaticResource myTemplateSelector}"
>
</ListBox>
</Grid>
Greetings and thanks :)
The TextBlock has an Inlines property that returns the Inline elements that comprise the contents of the TextBlock.
The Label has a Content property that you, depending on how you are using it, may cast to a Panel.
There are no inline elements for a TextBox.
Now, I found a solution. I made the TextBlock and Label as User Control and set the Name-property. In the code-behind, I have access to the DataContext and the element can set itself.
<UserControl x:Class="Test.TextBlockControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TextBlockControl"
Loaded="UserControl_Loaded">
<Grid>
<StackPanel HorizontalAlignment="Stretch" Margin="0,0,0,0">
<TextBlock Name="textBlock"/>
</StackPanel>
</Grid>
In the Code behind i can now access the values and set:
public partial class TextBlockControl : UserControl
{
public List<string> name => DataContext as List<string>;
public TextBlockControl()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
foreach (var t in name)
{
var run = new Run(t.Text);
if (t.IsHighlighted)
{
run.Foreground = Brushes.Green;
}
else
{
run.Foreground = Brushes.Red;
}
textBlock.Inlines.Add(run);
}
}
}
}
In the MainWindow, the dataTemplate then references the UserControl (root is the namespace):
<root:PickControl />
Related
I have ResourceDictionary that holds Dialog template. It has own DataType="{x:Type viewModels:DialogViewModel}". I would like to set this dialog "window" size on initialization. I know how to do it if I will add for example Height property to DialogViewModel. However this is not the right place to specify Height. How to do it in code behind and then Bind to that property? I think I have tried all the possible solutions I was able to find.
Basically I need to specify Height in SplitDialog.xaml.cs, let's say Height=500 and then add it to <Grid Margin="20,20,20,10" Tag="Category dialog" MinHeight="450" Height="???" x:Name="MainGrid">, but how?
I have tried to add to SplitDialog.xaml.cs (returns Height is null):
Grid gGrid = (Grid)this.FindName("MainGrid");
gGrid.Height = 600;
SplitDialog.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Controls.Styles.Dialog.SplitDialog"
xmlns:viewModels="clr-namespace:ViewModels.Category;assembly=ViewModels">
<DataTemplate DataType="{x:Type viewModels:DialogViewModel}">
<DataTemplate.Resources>
<ResourceDictionary>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</ResourceDictionary>
</DataTemplate.Resources>
<Grid Margin="20,20,20,10" Tag="Category dialog" MinHeight="450" x:Name="MainGrid">
</Grid>
</DataTemplate>
</ResourceDictionary>
SplitDialog.xaml.cs:
public partial class SplitDialog : ResourceDictionary
{
public SplitDialog()
{
}
}
Provide this logic in Loaded event of the Grid, e.g:
<Grid Loaded="Grid_Loaded">...
then in code-behind:
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
Grid grid = sender as Grid;
if (grid != null)
{
grid.Height = 600;
}
}
no?
XAML
<Page x:Class="ManufacturingWPF.ShowHardware"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ManufacturingWPF"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="ShowHardware">
<Grid Background="AliceBlue">
<ListBox x:Name="HardwareList" ItemsSource="{Binding Hardware}" HorizontalAlignment="Left" Height="122" Margin="76,36,0,0" VerticalAlignment="Top" Width="149">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding ID}"/>
<TextBlock Text="{Binding Date}"/>
<TextBlock Text="{Binding Nodes}"/>
<TextBlock Text="{Binding Repeaters}"/>
<TextBlock Text="{Binding Hubs}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Code Behind C#
public partial class ShowHardware : Page
{
public ShowHardware()
{
InitializeComponent();
DisplayData();
}
public void DisplayData()
{
//Datamodel MDM used for ADO and table creation
//Test is a class used to pass the model and as the name suggest
test it
ManufacturingDataModel MDM = new ManufacturingDataModel();
Test t = new Test(MDM);
List<Hardware> x = t.GetHardware();
foreach(Hardware i in x )
{
HardwareList.ItemsSource = i.Hubs.ToString();
}
}
}
}
I'm facing issues binding the data to the listbox as shown in the XAML and code-behind content.
I've tried previous answers without any luck , did my research but apparently I'm missing out something or maybe there is something I don't quite understand.
Itemsource as the name suggest should bind to the source of where my data is being held. In this case the source would be my class Hardware that holds data for Nodes, date , hubs etc etc.
And in the textblock I manually bind these properties and display the values.
But this is not working.
P.S. My DB table is populated.
That's because the ItemsSource is an IEnumerable and you assign to it the list of hardware itself. So you code should look something like this :
ManufacturingDataModel MDM = new ManufacturingDataModel();
Test t = new Test(MDM);
List<Hardware> x = t.GetHardware();
HardwareList.ItemsSource = x;
//or
foreach (Hardware h in x)
HardwareList.Items.Add(h);
This code seems wrong
foreach(Hardware i in x )
{
HardwareList.ItemsSource = i.Hubs.ToString();
}
Then ItemsSource for Binding has to be a Collection (List, ObservableCollecion, IEnumerable<> ...).
Try HardwareList.ItemsSource = x; and remove the foreach loop
I hope this can help you.
In my WPF application, I have a ListBox in my main screen. I'm trying to use the MVVM pattern, so I have a ViewModel associated with the View. When I launch the application, my ViewModel gets initiated, and it reads in a bunch of DLLs I've placed in a directory. Each DLL contains a "Strategy" class, so when I read the DLLs, I retrieve these Strategy class objects and put them in a list (actually an ObservableCollection) which is a member of my ViewModel. I'm using this member list, named DllList, to populate the ListBox.
My ViewModel looks like the following (unnecessary bits removed for clarity):
public class ViewModelPDMain : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName) {
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public ViewModelPDMain() {
dllList = new ObservableCollection<Strategy>();
selectedStrategy = new Strategy();
}
private ObservableCollection<Strategy> dllList = null;
private Strategy selectedStrategy = null;
public ObservableCollection<Strategy> DllList
{
get { return dllList; }
set {
dllList = value;
RaisePropertyChanged("DllList");
}
}
public Strategy SelectedStrategy
{
get { return selectedStrategy; }
set {
selectedStrategy = value;
RaisePropertyChanged("SelectedStrategy");
}
}
}
Then in my main View, I bind it as follows.
<Window x:Class="PrisonersDilemma.Source.View.ViewPDMain"
xmlns:local="clr-namespace:PrisonersDilemma.Source.View"
DataContext="{Binding Source={StaticResource mainViewModelLocator}, Path=ViewModelPDMain}"
Title="Iterated Prisoner's Dilemma" Height="500" Width="800" MinHeight="500" MinWidth="800">
<Grid Name="gridMain">
...
<!-- More stuff here -->
...
<ListBox Name="listStrategies" SelectedIndex="0"
ItemsSource="{Binding DllList}" SelectedItem="{Binding SelectedStrategy}"
Grid.Column="0" Grid.Row="1" Grid.RowSpan="2"
Width="Auto" MinWidth="120"
Margin="3"
BorderBrush="LightGray" BorderThickness="1">
</ListBox>
...
<!-- More stuff here -->
...
</Grid>
</Window>
When I do this and run the application my list box looks like below which is expected.
The problem is when I try to display a property inside my Strategy objects. My Strategy class contains another class, named StratInfo, which in turn contains a string property, StrategyName. My requirement is to display this string value as listbox item values instead of what you can see above.
So I do the following in my View:
<Window x:Class="PrisonersDilemma.Source.View.ViewPDMain"
xmlns:local="clr-namespace:PrisonersDilemma.Source.View"
DataContext="{Binding Source={StaticResource mainViewModelLocator}, Path=ViewModelPDMain}"
Title="Iterated Prisoner's Dilemma" Height="500" Width="800" MinHeight="500" MinWidth="800">
<Grid Name="gridMain">
...
<!-- More Stuff Here -->
...
<ListBox Name="listStrategies" SelectedIndex="0"
ItemsSource="{Binding DllList}" SelectedItem="{Binding SelectedStrategy}"
Grid.Column="0" Grid.Row="1" Grid.RowSpan="2"
Width="Auto" MinWidth="120"
Margin="3"
BorderBrush="LightGray" BorderThickness="1">
<!-- Added Stuff -->
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Label Name="lblFirstName"
Content="{Binding SelectedStrategy.StratInfo.StrategyName, Mode=OneWay}"
Grid.Column="0"></Label>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
...
<!-- More Stuff Here -->
...
</Grid>
</Window>
When I do this, I expect the list box items to contain a label, and it to display my StrategyName value. However, I get a listbox which contains 25 items (I have 25 DLLs), but all 25 items are empty.
Funny thing is, I tried to bind the SelectedStrategy.StratInfo.StrategyName to a text box Text property, and it worked. That is, when I click any empty listbox item, it displays the StrategyName in the text box. Please refer to the following figure. You can see that the listbox contains items but the content values aren't displayed. In addition, to the right, the Strategy Name text box is a text box where I have bound the SelectedStrategy.StratInfo.StrategyName and it displays the correct value on item select event.
I have done this exact same thing in a simpler project, and it works just fine. I can't figure out what I'm doing wrong here.
Any thoughts?
Your binding in the data template is incorrect. The data context within the data template is an item in the DllList which is of type Strategy. So your Label should be like so:
<Label Name="lblFirstName"
Content="{Binding StratInfo.StrategyName, Mode=OneWay}"
Grid.Column="0"/>
I am working on windows phone 8 app.
I have List box with over 200 items to display.
<DataTemplate x:Key="DataTemplate1">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="White" Height="400" Width="400" CornerRadius="30,30,30,30">
</Border>
<Grid Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Top">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="5,20,5,5"
Foreground="#000000"
Text="{Binding Title}"/>
</Grid>
</Grid>
</DataTemplate>
But it crashes, i have debugged it till 100 items it works but after that it crashes.
In the PhoneApplicationPage_Loaded method i have
private void PhoneApplicationPage_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
myList.Add(new MyObject("A","A value"));
myList.Add(new MyObject("B", "B value"));
myList.Add(new MyObject("C", "C value"));
and so on... 200 items
ListBoxItems.ItemsSource = myList;
}
how can i fix this ?
Update :
<ItemsPanelTemplate x:Key="ItemsPanelTemplate">
<local:CollectionFlowPanel ItemHeight="400"
ItemWidth="400"
FocusedItemOffset="120"
UnfocusedItemOffset="20"
ItemVisibility="5">
<VirtualizingStackPanel />
</local:CollectionFlowPanel>
</ItemsPanelTemplate>
</phone:PhoneApplicationPage.Resources>
<Grid x:Name="LayoutRoot" Background="#000000">
<local:CollectionFlow x:Name="ListBoxItems"
ItemTemplate="{StaticResource DataTemplate}"
ItemsPanel="{StaticResource ItemsPanelTemplate}"/>
</Grid>
Ensure you have VirtualizingStackPanel inside the ItemsPanelTemplate of your list box, see this answer for more info.
Here's the XAML you likely need for your ListBox:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
You need to read following blog from msdn on visualization of the data in list and grid.
Using virtualization with a list or grid
Without seeing your whole xaml code I cannot suggest the exact answer but my guess is that you in xaml ListBox is placed inside a canvas/StackPanel or scrollviewer control.
When the size of the ItemsControl's viewport isn't restricted, the control doesn't perform virtualization. Instead, it creates an item container for each item in its collection. Some common containers that don't restrict the viewport size are Canvas, StackPanel, and ScrollViewer. You can enable virtualization in this situation by setting the size of ItemsControl directly, instead of letting it be sized by its parent container.
Here, we set the Height and Width on the GridView. This restricts the size of the viewport, and items outside of the viewport are virtualized.
Below are 2 scenarios one will throw out of memory exception and other will work fine(use your same code behind and test)
1. ListBox in Canvas
<Canvas .....
<ListBox Name="ListBoxItems".....
</ListBox>
</Canvas>
Above code will throw out of memory exception as items control's viewport is not defined (if you still want to use Canvas than define width/height if ListBox in that case the port of Items control is defined and virtulazation will apply)
2. ListBox in Grid
<Grid .....
<ListBox Name="ListBoxItems".....
</ListBox>
</Grid>
The above code will not throw out of memory exception as virtuallization is applied on the listbox.
Hope this will help
How big is your object ? If your object is too big you might not be able to load them all at once.
Did you try using the for loop?
public List<Fellow> fellowList { get; set; }
private void PhoneApplicationPage_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
fellowList = new List<Fellow>();
for (int i = 0; i < 2; i++)
{
Fellow fellow = new Fellow();
fellow.x = "B" + i;
fellow.value = "B Value" + i;
fellowList.Add(fellow);
}
this.DataContext = this;
ListBoxItems.ItemsSource = fellowList;
}
public class Fellow
{
public string x { get; set; }
public string value { get; set; }
}
Hope it helps..change the view model according to your wish
I have an application that has a lot of textboxes in it. Also this textboxes actually are never disabled, but rather become ReadOnly instead. I prefer using one property of ContentControl for all my controls, but if i set IsEnabled to false all my textboxes become disabled. How can i make them go to readonly mode? I prefer not making my own control though, maybe i can use styles or something to reassign behaviour?
Edit: I actually am looking for solution that allows me to use binding to bind state of all controls(IsReadOnly) through binding in one place. Like:
<ContentControl IsEnabled="{Binding boolFlag, Mode=OneWay}">
<Grid x:Name="LayoutRoot" HorizontalAlignment="Stretch">
....
<TextBox/>
</Grid>
</ContentControl>
It seems like the use of the DataForm control would be best in your case. It would allow you to control every field within it as a group. It does provide the IsReadOnly option plus it comes with many free features that are very nice.
This video gives a good introduction
http://www.silverlight.net/learn/data-networking/data-controls/dataform-control
http://www.silverlightshow.net/items/Creating-Rich-Data-Forms-in-Silverlight-3-Introduction.aspx
http://www.silverlight.net/content/samples/sl4/toolkitcontrolsamples/run/default.html
Look for dataform
Cheers,
I suggest you to use a simple extension method for your ContentControl that will make textBoxes IsReadOnly = True. For example:
public static class ContentControlEx
{
public static void DisableTextBoxes(this ContentControl contentControl)
{
FrameworkElement p = contentControl as FrameworkElement;
var ts = p.GetChildren<TextBox>();
ts.ForEach(a => { if (!a.IsReadOnly) a.IsReadOnly = true; });
}
public static List<T> GetChildren<T>(this UIElement parent) where T : UIElement
{
List<T> list = new List<T>();
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++) {
UIElement child = VisualTreeHelper.GetChild(parent, i) as UIElement;
if (child != null) {
if (child is T)
list.Add(child as T);
List<T> l1 = GetChildren<T>(child);
foreach (T u in l1)
list.Add(u);
}
}
return list;
}
}
usage (for ContentControl with Name = "content"):
content.DisableTextBoxes();
I have a XAML like this:
<Grid x:Name="LayoutRoot" Background="White">
<ContentControl IsEnabled="True" Name="content">
<StackPanel Margin="15">
<TextBox Width="150" Name="tb1" Margin="5" Text="{Binding tb1}" />
<TextBox Width="150" Name="tb2" Margin="5" Text="{Binding tb2}" />
<TextBox Width="150" Name="tb3" Margin="5" Text="{Binding tb3}"/>
<TextBox Width="150" Name="tb4" Margin="5" Text="{Binding tb4}"/>
<Button Name="bSubmit" Click="bSubmit_Click">Make Textboxes readonly</Button>
</StackPanel>
</ContentControl>
</Grid>
Let me know if it helps...
If I understand right, you want to bind each TextBox.IsReadOnlyProperty to a single bool.
You might try something like this, in a similar fashion to how you bound the IsEnabled property of the ContentControl:
<TextBox IsReadOnly="{Binding boolFlag, Mode=OneWay}" ... /> <!-- in each of your textboxes -->
This should provide you what you need: change boolFlag, every textbox turns on or off.