ListBox within WPF TextBox - Chips - wpf

I need to add ListBox within a TextBox i.e., CHIPS
Refer the Screen shot: (Expectation)
Just Consider the View Model:
public class Person
{
private ObservableCollection<string> _personList = new ObservableCollection<string>();
public ObservableCollection<string> PersonList
{
get { return _personList; }
set
{
_personList = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("PersonList"));
}
}
private string _personStr = String.Empty;
public string PersonStr
{
get { return _personStr; }
set
{
_personStr = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("PersonStr"));
}
}
public Person()
{
PersonList.Add("IR-Punch");
PersonList.Add("Stack-Overflow");
}
public ICommand BTextCommand
{
get
{
return new DelegateCommand(AppendString);
}
}
public void AppendString()
{
PersonList.Add(PersonStr);
}
}
The working XAML Source Code:
<ItemsControl ItemsSource="{Binding PersonList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding }" TextWrapping="Wrap"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBox Text="{Binding PersonStr}" Width="160" VerticalAlignment="Center" />
<Button Command="{Binding BTextCommand}" Content="Add" />
Kindly assist me how to add ListBox within a TextBox. I take care of Sytle. I'm expecting the core idea.

Don't try to embed additional controls within the TextBox, instead embed the ItemsControl and TextBox in a stack panel as shown below:
<Border BorderThickness="1">
<ScrollViewer VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto">
<StackPanel Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding PersonList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBox MinWidth="100" BorderBrush="Transparent" BorderThickness="0" Text="{Binding PersonStr}" />
</StackPanel>
</ScrollViewer>
</Border>
You will want to style the outer Border to look like a TextBox (pretty much just finding the correct BorderBrush) but, as requested, I've left the styling to you.
Please note, I've not actually tried this solution, just penned it as an approach that should work. Let me know if you have any problems with it.

Related

Binding several ItemsControl to one ObservableCollection including visibility

In general, we can not bind multiple controls to one ObservableCollection
Is it possible to do this in the following situation?
Only one part is visible at a time
In this situation, there is a reference error twice to the same collection
How does it actually work internally? Should it not include invisible code?
<Grid Visibility="{Binding B1Visible, Converter={StaticResource BooleanToVisibilityConverter}}">
<ItemsControl ItemsSource="{Binding Elements, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<!--Another code for B1-->
</Grid>
<Grid Visibility="{Binding B2Visible, Converter={StaticResource BooleanToVisibilityConverter}}">
<ItemsControl ItemsSource="{Binding Elements, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<!--Another code for B2 -->
</Grid>
I don't know what Elements is, and I don't know where you're headed using a grid for the itemspanel of your itemscontrol.
But.
You can bind to the same observablecollection multiple times.
In the code below the two visibility properties are Boolean and the converter translates true into Visibility.Visible and false into Visibility.Collapsed.
public class MainWindowViewModel : BaseViewModel
{
private ObservableCollection<Person> people = new ObservableCollection<Person>();
public ObservableCollection<Person> People
{
get { return people; }
set { people = value; RaisePropertyChanged();}
}
private bool b1Visible = true;
public bool B1Visible
{
get { return b1Visible; }
set { b1Visible = value; RaisePropertyChanged(); }
}
private bool b2Visible = true;
public bool B2Visible
{
get { return b2Visible; }
set { b2Visible = value; RaisePropertyChanged(); }
}
public MainWindowViewModel()
{
People.Add(new Person { FirstName = "Chesney", LastName = "Brown" });
People.Add(new Person { FirstName = "Gary", LastName = "Windass" });
People.Add(new Person { FirstName = "Liz", LastName = "McDonald" });
People.Add(new Person { FirstName = "Carla", LastName = "Connor" });
}
}
My markup:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<StackPanel>
<Grid Visibility="{Binding B1Visible, Converter={StaticResource BooleanToVisibilityConverter}}">
<ItemsControl ItemsSource="{Binding People}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding LastName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!--Another code for B1-->
</Grid>
<Grid Visibility="{Binding B2Visible, Converter={StaticResource BooleanToVisibilityConverter}}">
<ItemsControl ItemsSource="{Binding People}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding LastName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</StackPanel>
Both itemscontrols are visible and so I see the list of names twice.

wpf datepicker visibility binding dont work

I want to code in xaml all elements but by code-behind showing and hiding some parties depend on step.
here i modified code to been understendable so i putted both propriety to collapsed
question is about visibility of itemscontrol and datepicker cause second one never take value from codebehind
having this code:
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Name="d1">
<ItemsControl Name="Panel" ItemsSource="{Binding Content}" Visibility="{Binding Panel_Visibility}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel HorizontalAlignment="Center" VerticalAlignment="Center"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Height="100" Width="100" Margin="5" Content="{Binding Content}" Command="{Binding Button_Click}" CommandParameter="{Binding Content}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
<StackPanel Grid.Row="1" Name="d2">
<DatePicker Name="DateVoyage" Visibility="{Binding DateVoyage_Visibility}"/>
</StackPanel>
</Grid>
and having :
class Page_Modele : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
...
private Visibility panel_visibility;
public Visibility Panel_Visibility {
get { return this.panel_visibility; }
set {
this.panel_visibility = value;
OnPropertyChanged("Panel_Visibility");
}
}
private Visibility datevoyage_visibility;
public Visibility DateVoyage_Visibility {
get { return this.datevoyage_visibility; }
set {
this.datevoyage_visibility = value;
OnPropertyChanged("DateVoyage_Visibility");
}
}
...
public Page_Modele()
{
this._data = new donnees();
Panel_Visibility = Visibility.Collapsed;
DateVoyage_Visibility = Visibility.Collapsed;
this.Content = itemsourc.Get_itemsourc(Execute1_ButtonClick, this._data.getContinentsList());
}
...
}
ItemsControl is collapsed, but DatePicker not.
What i am missing?

WPF XAML ListBox bind to array

So i have a float array which i want to have as ItemSource in a ListBox.
Inside the ItemTemplate i have a progress bar, that should bind its Progress value to the given float value. Yet i can't ever see that the values are actually bound to the Progress property.
The xaml code (i don't know whether i'm wrong but i expected that there's a implicit cast from float to double):
<ListBox ItemsSource="{Binding CoreLoads, Mode=OneWay}" BorderThickness="0">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type sys:Double}">
<StackPanel>
<ctrl:MetroProgressBar Orientation="Vertical" Progress="{Binding}" ExtenedBorderWidth="0.2" Width="30" Height="50" VerticalAlignment="Center"
HorizontalAlignment="Center" BorderBrush="Black" BorderThickness="2" Background="White" Margin="5"/>
<TextBlock Margin="0,3,0,3" HorizontalAlignment="Center" Text="{Binding LastUpdateTime, StringFormat='{}{0:hh:mm:ss tt}', Mode=OneWay}"
DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
xmlns:sys="clr-namespace:System;assembly=mscorlib"
and the property itself:
public double[] CoreLoads
{
get { return cpuManagement.ProcessorInfo.LoadPercentages; }
}
Note: The progress bar i'm using is a custom control and inherits from System.Windows.Controls.Control.
Problem seems to be that the values of the array are ok but when bound to a TextBlock the values are 0, so the progress of the progress bar is always 0. So, am i having a correct data template for a double array? Or should i change to another type of collection?
I guess you should create a property in a ViewModel(assuming you're using MVVM pattern) which will represent selected value of the ListBox:
private double selectedCoreLoad;
public Double SelectedCoreLoad
{
get
{
return selectedCoreLoad;
}
set
{
if (selectedCoreLoad != value)
{
selectedCoreLoad = value;
RaisePropertyChanged("SelectedCoreLoad");
}
}
}
Then, you should bind selected value of the ListBox to this property:
<ListBox ItemsSource="{Binding CoreLoads, Mode=OneWay}" SelectedValue="{Binding SelectedCoreLoad, Mode=TwoWay}" BorderThickness="0">
<ctrl:MetroProgressBar Orientation="Vertical" Progress="{Binding SelectedCoreLoad}" ExtenedBorderWidth="0.2" Width="30" Height="50" VerticalAlignment="Center"
HorizontalAlignment="Center" BorderBrush="Black" BorderThickness="2" Background="White" Margin="5"/>
UPD
Use ObservableCollection instead:
private ObservableCollection<Double> coreLoads;
public ObservableCollection<Double> CoreLoads
{
get { return coreLoads; }
set
{
coreLoads = value;
RaisePropertyChanged("CoreLoads");
}
}
So found the answer: ListBox seems not to like arrays as ItemsSource. After changing the source to a double-list everything works.
public List<double> CoreLoads
{
get { return new List<double>(cpuManagement.ProcessorInfo.LoadPercentages); }
}

WPF Textbox TwoWay binding in datatemplate not updating the source even on LostFocus

I have an ObservableCollection<string> Tags as part of a custom object. I bind it to a DataTemplate in order to show all tags to the user with the following code:
<StackPanel DockPanel.Dock="Top" Margin="15,0,15,0" Orientation="Horizontal">
<Label Content="Tags:" FontSize="14" Foreground="{StaticResource HM2LightTextBrush}"/>
<Grid>
<ItemsControl Name="PanelPreviewNoteTags" ItemsSource="{Binding ElementName=lbNotesQuickView, Path=SelectedItem.Tags}" Margin="3,0" Visibility="Collapsed">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1" BorderBrush="#676B6E" Margin="3,0">
<Label Content="{Binding .,Mode=OneWay}" Foreground="{StaticResource HM2LightTextBrush}"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl Name="PanelEditNoteTags" ItemsSource="{Binding ElementName=lbNotesQuickView, Path=SelectedItem.Tags}" Margin="3,0" Visibility="Collapsed">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1" BorderBrush="#676B6E" Margin="3,0">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding ., Mode=TwoWay}"/>
<Button Style="{StaticResource RibbonButton}" Click="ButtonRemoveTagClick" Tag="{Binding}">
<Image Height="16" Width="16" Source="/Poker Assistant;component/Resources/fileclose.png" />
</Button>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</StackPanel>
Adding and removing items from the ObservableCollection works as expected.
In code I switch between edit and view mode by setting the Visibility of the corresponding PanelEditNoteTags and PanelPreviewNoteTags. This all good and working. But when I enter the edit mode and start typing new values for the tags in the TextBox the source doesn't get updated. I certainly know that the LostFocus event is raised when I press my Save button. I tried all UpdateSourceTrigger values, still the same.
Is it a problem related to two controls binding at the same time to the same value - the Label from PanelPreviewNoteTags and the TextBox from PanelEditNoteTags?
What am I missing here?
#Clemens Thank you for the quick and accurate response :) Following is the working solution for future reference.
The solution is not to use ObservableCollection<string> Tags because as pointed by Clemens the {Binding ., Mode=TwoWay} does not work back to the source.
So I created a custom Tag class:
public class Tag : INotifyPropertyChanged
{
private string _content;
public string Content { get { return _content; } set { _content = value; OnMyPropertyChanged(() => Content); } }
public Tag(string content)
{ Content = content; }
public Tag()
: this("new tag")
{ }
public event PropertyChangedEventHandler PropertyChanged;
// Raise the event that a property has changed in order to update the visual elements bound to it
internal void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
//CONVERTS the passed parameter to its name in string
internal void OnMyPropertyChanged<T>(Expression<Func<T>> memberExpression)
{
MemberExpression expressionBody = (MemberExpression)memberExpression.Body;
OnPropertyChanged(expressionBody.Member.Name);
}
public override string ToString()
{
return Content;
}
}
And use it as ObservableCollection<Tag> Tags. Then bind to it like so
<TextBox Text="{Binding Content, Mode=TwoWay}" Tag="{Binding}"/>
I actually populate from and save to postgres database in a string array column, so I need to convert to and from string[]. These are my conversions:
string[] array = note.Tags.Select(item => item.Content).ToArray();
note.Tags = new ObservableCollection<Tag>((array.Select(item => new Tag() { Content = item }).ToList()));

How to put an Expander inside Datatemplate?

Strange one.
I have a contentcontrol on a WPF form, this loads a datatemplate within it.
This shows up fine (handwritten summary code so ignore errors/lack of attributes):
<DataTemplate>
<Label Content="Found datatemplate" />
</DataTemplate>
This however renders blank
<DataTemplate>
<Expander Header="Why dont I show">
<Label Content="Found datatemplate" />
</Expander>
</DataTemplate>
I have set the expander to visibile, isexpanded to true etc and no matter what it doesn't render at all.
Confused- is this just not possible?
I've recently done something similar to what you're describing and it worked for me. I have an ItemsControl that binds to a collection of view models, each of which contains a UserControl representing custom content. I implemented the ItemsControl.ItemTemplate to display the custom control inside an Expander like this:
<ItemsControl Margin="0,20,0,0" ItemsSource="{Binding ControlItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="0,0,0,0"
BorderBrush="#E7E7E7"
BorderThickness="0,1,0,0"
Padding="20,0">
<Expander Foreground="#E7E7E7"
IsExpanded="{Binding Path=IsExpanded,
Mode=TwoWay}">
<Expander.Header>
<Grid>
<TextBlock HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="24"
Text="{Binding Title}" />
</Grid>
</Expander.Header>
<DockPanel>
<ScrollViewer MinHeight="250">
<ContentControl Content="{Binding Control}" />
</ScrollViewer>
</DockPanel>
</Expander>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This is what my view model looks like:
public class SidePanelControlItem : ModelBase
{
private bool _isExpanded;
public SidePanelControlItem(UserControl control)
{
if (control == null) { throw new ArgumentNullException("control");}
Control = control;
}
public string Title { get; set; }
public UserControl Control { get; private set; }
public bool IsExpanded
{
get { return _isExpanded; }
set
{
_isExpanded = value;
OnPropertyChanged("IsExpanded");
}
}
}

Resources