Binding an entire collection object and subproperties without setting DataContext - c#

I´m trying to bind a ListBox to a ObservableCollection. I wan´t to bind the Text Properties of the ListBox entrys and the Background of the ListBox entrys.
The ListBox is defined in an loaded loose xaml file:
<TextBox Margin="0,5,5,5" Text="{Binding Path=TB9P}" Background="LightBlue" Name="DetailsviewTB9" Height="20">
<TextBox.ToolTip>
<StackPanel>
<Label FontWeight="Bold" Background="Blue" Foreground="White">Daten</Label>
<ListBox ItemsSource="{Binding Source={StaticResource res_LB1P}}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=StringP}" Background="{Binding Path=SelectedItemP, Converter={StaticResource c_SelectedItemToBackgroundConverter}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</TextBox.ToolTip>
</TextBox>
The DataContext is set on class DetailsViewText
public class LBEntry
{
bool DetailsViewLBSelectedItem = true;
string DetailsViewLB = "test";
public LBEntry(bool selcected, string str)
{
DetailsViewLB = str;
DetailsViewLBSelectedItem = selcected;
}
public bool SelectedItemP
{
get { return DetailsViewLBSelectedItem; }
set { DetailsViewLBSelectedItem = value; }
}
public string StringP
{
get { return DetailsViewLB; }
set { DetailsViewLB = value; }
}
}
public class LBEntrysCollection : System.Collections.ObjectModel.ObservableCollection<LBEntry>
{
//
}
public class DetailsViewText
{
string[] DetailsViewTB1_Text = new string[20];
bool[] fDetailsViewCB = new bool[20];
LBEntrysCollection[] LBEntrys = new LBEntrysCollection[]{
new LBEntrysCollection{ new LBEntry(false, "test"), new LBEntry(true, "test") },
new LBEntrysCollection{ new LBEntry(true, "test") },
new LBEntrysCollection{ new LBEntry(false, "test") },
new LBEntrysCollection{ new LBEntry(false, "test") },
new LBEntrysCollection{ new LBEntry(false, "test") }
};
public LBEntrysCollection LB1P
{
get { return LBEntrys[0]; }
set { LBEntrys[0] = value; }
}
public string TB9P
{
get { return DetailsViewTB1_Text[8]; }
set { DetailsViewTB1_Text[8] = value; }
}
...
}
The resource res_LB1P is set in the mainWindow constructor:
// Resources
this.Resources.Add("res_LB1P", detailsViewFrameHandling.DetailsViewTextP.LB1P);
Basicly I just want to bind the ListBox to a LBEntrysCollection with SelectedItemP as switch for the background Color and StringP as the Text Property. But I need the DataContext on DetailsViewText for other Propertys.
I´m getting an Exception when the xaml File is loading the StaticResource res_LB1P.
How do I have to set my Binding on ListBox and TextBlock to get it right?
EDIT:
With this
<ListBox ItemsSource="{Binding Path=LB1P}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=LB1P.StringP}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Items are added, but there is no Text shown in the TextBox
Now I´m really confused. It does work like this:
<ListBox ItemsSource="{Binding Path=LB1P}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=StringP}" Background="{Binding Path=SelectedItemBrushP}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Simple enough, but I thought i had tried this before and it didn´t work...
Is it possible, that if one Binding does fail (the Background Binding) the other Binding (Text Property) does also not work?

I have always considered the ViewModel (the object the DataContext points to) to be just that: a Model of the View.
So to solve this, you need either one object that will be the ViewModel because there is only one DataContext property or you will need to add an extra DataContext-like property.
The first option (one ViewModel) can be realized by creating a new class that contains both the ObservableCollection and the DetailsViewText:
class ComposedViewModel: INotifyPropertyChanged
{
public LBEntrysCollection LBEntries
{
get { ... }
set { ... }
}
public DetailsViewText Details
{
get { ... }
set { ... }
}
}
The second option (extra DataContext-like property) can be realized by sub-classing the ListBox and adding another property.

Why not do this ?
<ListBox ItemsSource="{Binding ElementName=<TextBox's Name>, Path=DataContext">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=StringP}" Background="{Binding Path=SelectedItemP, Converter={StaticResource c_SelectedItemToBackgroundConverter}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Correct me if I'm wrong with understanding your question. You want to bind the listbox's itemssource to the textbox's datacontext?

Related

Changing the ViewModel's property from another ViewModel

I have the ListBox on my MainView.xaml, selecting the Item forces the ContentControl to display different UserControls. I use Caliburn.Micro library in this propgram. Here's some code:
<ListBox Grid.Row="1" Grid.Column="1" x:Name="ItemsListBox" SelectedItem="0" ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding TextBlock1Text}" x:Name="TextBlock1"/>
<ContentControl Grid.Row="3" Grid.Column="1" Content="{Binding ElementName=ItemsListBox, Path=SelectedItem.Content}" />
The MainViewModel.cs:
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
NotifyOfPropertyChange(() => Name);
}
}
private string _textBlock1Text;
public string TextBlock1Text
{
get => _textBlock1Text;
set
{
_textBlock1Text = value;
NotifyOfPropertyChange(() => TextBlock1Text);
}
}
public MainViewModel()
{
TextBlock1Text = "Test";
Items = new ObservableCollection<ItemsModel>()
{
new ItemsModel { Name="Useless", Content=null },
new ItemsModel { Name="TextChangerViewModel", Content=new TextChangerViewModel(TextBlock1Text) }
};
}
public ObservableCollection<ItemsModel> Items { get; set; }
The ItemsModel.cs:
public class ItemsModel
{
public string Name { get; set; }
public object Content { get; set; }
}
And finally the TextChangerViewModel.cs:
public class TextChangerViewModel : Conductor<object>
{
private string _textBlock1Text;
public string TextBlock1Text
{
get => _textBlock1Text;
set
{
_textBlock1Text = value;
NotifyOfPropertyChange(() => TextBlock1Text);
}
}
public TextChangerViewModel(string textBlock1Text) //passing parameter from another ViewModel
{
TextBlock1Text = textBlock1Text;
}
}
So, the main question is how to change the TextBlock1Text (and the Text value of TextBlock in .xaml as well) in the MainViewModel.cs from the TextChangerViewModel.cs? I was thinking about using something like NotifyCollectionChanged on my Items ObservableCollection, but it work with collection of ItemsModel, not with the VM's, so I'm stuck here.
I'm also not sure if having public object Content { get; set; } in ItemsModel.cs is a good thing if I'm targeting the MVVM pattern, but I don't know the other way to do it (I'm very new to MVVM).
UPD
I'm looking for the property-changing way because I need to change the TextBlock1Text Text from another UserControl. Suppose I have the button on my TextChangerView.xaml: <Button Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Content="Change da text" cal:Message.Attach="ChangeTextButton"/>
And after the click on it I want the text on the parental MainView.xaml to change. But the thing is, I don't know how to change properties in this case, as I wrote above why.
Change the the binding of textblox1 to reference the selected item.
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding ElementName=ItemsListBox, Path=SelectedItem.Name}" x:Name="TextBlock1"/>
or
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding ElementName=ItemsListBox, Path=SelectedItem.Content.TextBlock1Text}" x:Name="TextBlock1"/>

Bind a value from code behind to datatemplate control in wpf

I'm new to wpf, I created a listbox it will create a dynamic listitems,Here I used datetemplate which contains two controls that is two textblocks, one textblocks contains binding a values form combobox(which is string datatype),The other one is, bind a value from code bind.
XAML
<ListBox ItemsSource="{Binding obj}" HorizontalContentAlignment="Left" x:Name="lstbxindex" SelectionMode="Extended" Foreground="White" FontSize="20px" Height="201" BorderBrush="#555555" Margin="80,40,0,0" VerticalAlignment="Top" Width="282" Background="#555555" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5" >
<TextBlock Height="40px" Width="80px" Text="{Binding roundedhourvalue, FontSize="24" Background="#555555" Foreground="White"></TextBlock>
<TextBlock x:Name="items" Text="{Binding}" Margin="35,0,0,0"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
C# (Roundedhour.cs)
public class Roundedhour
{
public string hourvalue { get; set; }
public override string ToString()
{
return string.Format("{0}", hourvalue);
}
}
In this class create a property for hourvalue. For this class I created a object in codebehind file which I mentioned below.create a object and assign a value for hourvalue variable.
C# (Code Behind)
{
if (dispatcherTimer1.Interval == TimeSpan.FromSeconds(15))
{
//lstbxindex.Items.Add(lstbxindex.SelectedItem.ToString());
string hrvalue = Convert.ToString(hrvalueinitially);
obj = new Roundedhour();
obj.hourvalue = Convert.ToString(hrvalueinitially);
string roundedhourvalue =obj.hourvalue;
this.DataContext = this;
//lblprojectAhr.Content = string.Join(",", hrvalueinitially + "" + "hr");
}
}
Here, I created a object for Rounderhour class.Assign values to that property hour value. But I cannot be bind a value from codebind to XAML page.
Your ItemsSource should be of an CollectionType.
ListBox ItemsSource="{Binding obj}"
You should also start to give your variables and properties meaningful names. That makes it easier to read your code later on.
The second Problem is in your Binding itself.
You are binding like this: Text="{Binding roundedhourvalue}
So WPF is expecting a property roundedhourvalue on obj.
But as shown in your CodeBehind there is only obj.hourvalue.
So change your Binding to Text="{Binding hourvalue}
Check your Output-Window for details.
NOTE:
string roundedhourvalue = obj.hourvalue;
has no getter and is not accsessible since its private.
NOTE: You either use a Binding OR your set the ItemsSource in CodeBehind.
Try it like this:
Just remove all the formatting and stuff:
<ListBox ItemsSource="{Binding RoundedHours}" x:Name="ListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5" >
<TextBlock Text="{Binding hourvalue}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And change your code-behind to this:
private void UpdateDataContext(object hrvalueinitially)
{
List<Roundedhour> hours = new List<Roundedhour>();
hours.Add(new Roundedhour()
{
hourvalue = hrvalueinitially.ToString()
});
//Set the ItemsSource in code: => remove your ItemsSource Binding from XAML
listBox.ItemsSource = hours;
}
OR your can use an 'MVVM' approach:
public class MyViewModel : INotifyPropertyChanged
{
//IMPLEMENT INotifyPropertyChanged HERE PLS
public ObservableCollection<RoundedHour> Collection { get; set; } = new ObservableCollection<RoundedHour>();
private void AddToCollection(object hrvalueinitially)
{
Collection.Add(new RoundedHour()
{
hourvalue = hrvalueinitially.ToString()
});
OnPropertyChanged("Collection");
}
//Make sure to set your Windows DataContext to an Instance of this Class
}
Assign XAML object's "ItemsSource" property with your binding variable.
Also it's totally wrong binding object's itself into object's property like
this.DataTemplate = this;
Use:
List<yourobject> bindingObjectList = new List<yourobject>();
// insert your objects into the list
this.ItemsSource = bindingObjectList;
Here you can find an example:
Grid & Pivot Binding Example for multiple DataTemplates

Populate a Tree View with a Custom List Object

I have a custom object that consists of 2 properties. The first is a string that i wish to use as a summary or header in the tree view. The second is a list of a custom type that contains objects that are to be included under each header. The objects contain things such as name, id, area, etc.. Ill most likely default to the name property of those list objects. How can I push this into a tree view.
Concatenated Model
public class WVWellModel : Notifier
{
private string _API;
public string API
{
get
{
return this._API;
}
set
{
this._API = value; OnPropertyChanged("API");
}
}
private string _WellName;
public string WellName
{
get
{
return this._WellName;
}
set
{
this._WellName = value; OnPropertyChanged("WellName");
}
}
private string _Division;
public string Division
{
get
{
return this._Division;
}
set
{
this._Division = value; OnPropertyChanged("Dvision");
}
}
private string _Area;
public string Area
{
get
{
return this._Area;
}
set
{
this._Area = value; OnPropertyChanged("Area");
}
}
private string _FieldOffice;
public string FieldOffice
{
get
{
return this._FieldOffice;
}
set
{
this._FieldOffice = value; OnPropertyChanged("FieldOffice");
}
}...............
** Model that will be put in a list to be injected into tree view**
public class groupingModel : Notifier
{
private string _Header;
public string Header
{
get { return _Header; }
set { _Header = value; OnPropertyChanged("Header"); }
}
private List<WVWellModel> _Wells;
public List<WVWellModel> Wells
{
get { return _Wells; }
set { _Wells = value; OnPropertyChanged("Wells"); }
}
}
List of Custom Type to be injected into tree view
List treeViewList = someMethod();
In summary, I would like to bind my tree view to a custom list object.List<groupingModel> The object in those lists have two properties, a string header that is to be used to group the objects in the tree view, and a second property that contains a list of custom objects "WVWellModel".
EDIT TO XAML to Allow Selection of all items in group
I've attempted to go ahead and make the group selectable with he goal that if the group is selected all children are selected underneath. Ive successfully bound it to a property inside of the group called "IsChecked". it defaults to false and works successfully. The problem is i am unable to capture the change in value and thus cannot run any logic to select its children.
<TreeView DockPanel.Dock="Bottom" ItemsSource="{Binding Groups}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="wellModel:WellGroupModel" ItemsSource="{Binding Wells}">
**<CheckBox Content="{Binding Header}" IsChecked="{Binding IsChecked}"/>**
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate DataType="{x:Type wellModel:WellModel}">
<CheckBox Content="{Binding WellName}" IsChecked="{Binding IsSelected}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
The TreeView control uses HierarchicalDataTemplate to control how items are displayed and how their children are populated. If your item class has children of a different type, it can specify its own child ItemTemplate, and so on recursively.
I've also added a minimal top-level viewmodel which owns a collection of GroupingModel. I'm using conventional C# naming conventions: Classes and properties start with a capital letter, private fields start with an underscore and a lower-case letter. It seems silly but when everybody uses the same convention, you always know what you're looking at.
Finally, I used ObservableCollection<T> rather than List<T>. If you bind an ObservableCollection to a control, then you can add/remove items in the collection and the control will automatically be notified and update itself without any additional work on your part.
XAML
<TreeView
ItemsSource="{Binding Groups}"
>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate
DataType="{x:Type local:GroupingModel}"
ItemsSource="{Binding Wells}"
>
<TextBlock Text="{Binding Header}" />
<HierarchicalDataTemplate.ItemTemplate>
<!-- This can be DataTemplate if no child collection is specified -->
<DataTemplate
DataType="{x:Type local:WVWellModel}"
>
<TextBlock Text="{Binding WellName}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Alternatively, if you have heterogeneous collections of objects, you can create implicit templates as resources and let them be applied by type rather than by hierarchy. In your particular case, this will produce identical results, because you have a strict item hierarchy.
<TreeView
ItemsSource="{Binding Groups}"
>
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type local:GroupingModel}"
ItemsSource="{Binding Wells}"
>
<TextBlock Text="{Binding Header}" />
</HierarchicalDataTemplate>
<DataTemplate
DataType="{x:Type local:WVWellModel}"
>
<TextBlock Text="{Binding WellName}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
C#
public class ViewModel : Notifier
{
public ViewModel()
{
Groups = new ObservableCollection<GroupingModel>
{
new GroupingModel {
Header = "First Group",
Wells = new List<WVWellModel> {
new WVWellModel() { WellName = "First Well" },
new WVWellModel() { WellName = "Second Well" },
new WVWellModel() { WellName = "Third Well" },
}
},
new GroupingModel {
Header = "Second Group",
Wells = new List<WVWellModel> {
new WVWellModel() { WellName = "Third Well" },
new WVWellModel() { WellName = "Fourth Well" },
new WVWellModel() { WellName = "Fifth Well" },
}
}
};
}
#region Groups Property
private ObservableCollection<GroupingModel> _groups = new ObservableCollection<GroupingModel>();
public ObservableCollection<GroupingModel> Groups
{
get { return _groups; }
set
{
if (value != _groups)
{
_groups = value;
OnPropertyChanged(nameof(Groups));
}
}
}
#endregion Groups Property
}
Update
Let's make the WVWellModel items checkable. First, we'll give them a boolean property that we'll bind to the checkbox's IsChecked property:
public class WVWellModel : Notifier
{
private bool _isSelected;
public bool IsSelected
{
get
{
return this._isSelected;
}
set
{
this._isSelected = value; OnPropertyChanged();
}
}
And then we'll change the content in the WVWellModel DataTemplate from a TextBlock to a CheckBox:
<DataTemplate
DataType="{x:Type local:WVWellModel}"
>
<CheckBox
Content="{Binding WellName}"
IsChecked="{Binding IsSelected}"
/>
</DataTemplate>
You can put any valid XAML UI in a template as long as there's a single root element.
<TreeView
Width="300"
Height="200"
ItemsSource="{Binding Groups}"
Grid.IsSharedSizeScope="True"
>
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type local:GroupingModel}"
ItemsSource="{Binding Wells}"
>
<TextBlock Text="{Binding Header}" />
</HierarchicalDataTemplate>
<DataTemplate
DataType="{x:Type local:WVWellModel}"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="CheckBoxColumn" />
<ColumnDefinition Width="Auto" SharedSizeGroup="APIColumn" />
</Grid.ColumnDefinitions>
<CheckBox
Grid.Column="0"
Content="{Binding WellName}"
IsChecked="{Binding IsSelected}"
/>
<TextBlock
Grid.Column="1"
Margin="12,0,0,0"
Text="{Binding API}"
HorizontalAlignment="Right"
/>
</Grid>
</DataTemplate>
</TreeView.Resources>
</TreeView>

Binding ObservableCollection<double> to ListBox not updating source [duplicate]

Edit: The basic problem is binding a List to ListBox(or any other control). So I am editing the question.
I bound a list of string to a ListBox as below. However when I change the contents of the textbox it is not changing the string in the source list.Why?
public partial class MainWindow : Window
{
List<string> _nameList = null;
public List<string> NameList
{
get
{
if (_nameList == null)
{
_nameList = new List<string>();
}
return _nameList;
}
set
{
_nameList = value;
}
}
public MainWindow()
{
NameList.Add("test1");
NameList.Add("test2");
InitializeComponent();
}
And the XAML
<ListBox Grid.Row="0" Grid.Column="0" DataContext="{Binding ElementName=main}" ItemsSource="{Binding NameList}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding .,Mode=OneWayToSource , UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The DataContext of each ListBoxItem is the string itself, so the path of your binding is empty (.). TwoWay and OneWayToSource bindings require a path, since you can't just replace the current DataContext. So you need to wrap your string in an object that exposes the string as a property:
public class StringItem
{
public string Value { get; set; }
}
Expose the strings as a list of StringItem:
public partial class MainWindow : Window
{
List<StringItem> _nameList = null;
public List<StringItem> NameList
{
get
{
if (_nameList == null)
{
_nameList = new List<StringItem>();
}
return _nameList;
}
set
{
_nameList = value;
}
}
public MainWindow()
{
NameList.Add(new StringItem { Value = "test1" });
NameList.Add(new StringItem { Value = "test2" });
InitializeComponent();
}
And bind to the Value property:
<ListBox Grid.Row="0" Grid.Column="0" DataContext="{Binding ElementName=main}" ItemsSource="{Binding NameList}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note that StringItem will also need to implement INotifyPropertyChanged so that bindings are automatically updated. You should also expose the list as an ObservableCollection<T> rather than a List<T>
May be it helsp?
<ListBox Name="lsbList">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Value}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
you can create a DataGridTemplateColumn.CellEditingTemplate with an itemscontrol and textboxes to edit your items
If I didn't misunderstand your question, it is pretty easy to implement. Look:
<ComboBox Text="My Comment 5 with addition." IsEditable="True" Height="25" Width="200">
<ComboBoxItem>My comment1</ComboBoxItem>
<ComboBoxItem>My comment2</ComboBoxItem>
</ComboBox>

Changing selected element in WPF

I'm completely new to WPF.
I'm making a simple application in the MVVM pattern.
I have a viewmodel with a model referenced in it. The model contains some netelements I want to put in a combobox.
Here is the relevant part of the viewmodel:
public class MainWindowVM : ViewModelBase
{
private Model _model = null;
public Model Model
{
get
{
return _model;
}
}
#region ActiveElement
private NetElement _activeElement = null;
public NetElement ActiveElement
{
get
{
return _activeElement;
}
set
{
if (_activeElement != value)
{
_activeElement = value;
RaisePropertyChanged("ActiveElement");
if (ActiveElementChanged != null)
ActiveElementChanged(this, EventArgs.Empty);
}
}
}
}
I would like to be able to select a NetElement in a combobox and set the ActiveElement to it.
here is the relevant part of my current XAML:
<ItemsControl Background="White" IsTabStop="True" ItemsSource="{Binding Path=Model.RootNet.Elements}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Margin="2,6">
<Hyperlink Command="{Binding Path=I'm not able to figure out what to write here}">
<TextBlock Text="{Binding Path=Name}" />
</Hyperlink>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This is not a combobox but a list of TextBlocks, but you can see where it is going.
How can I set the ActiveElement from the view?
Create a binding for the SelectedItem property of the ComboBox to your ActiveElement property:
<ComboBox SelectedItem="{Binding Path=ActiveElement}" ... />
then set the DataContext property of the view to your view model.

Categories

Resources