Bit of a newbie question but I can't seem to wrap my head around this one.
My structure is as follows
class Connection
{
private static ObservableCollection<Connection> conlist = getConnections();
private string _channel;
//other fields
public string Channel
{
get
{ return this._channel; }
}
public static ObservableCollection<Connection> Ports
{
get
{ return conlist; }
}
private ObservableCollection<Connection> getConnections(string path)
{
//Gets list of connections from a file
}
class Tab
{
private Connection _channel;
private string _title;
public string Title
{
get
{ return this._title; }
}
public string Connection
{
get { return this._channel; }
}
}
Now I want to have two comboboxes, one to select the Tab which will display the description and I want the other to display all the ports in Connection.Ports but have the selected value initially as the one in the Tab.Connection
<ComboBox x:Name="tab_combobox" ItemsSource="{Binding Tabs}" DisplayMemberPath="Title" Tag="{Binding Channel}" SelectedIndex="0"/>
<ComboBox x:Name="tab_channel_combobox"
ItemsSource="{Binding Source={x:Static model:Connection.Ports}}"
SelectedValue="{Binding SelectedItem.Tag, ElementName=tab_combobox}"
SelectedValuePath="Tag"
/>
Thank you in advance
Related
I have an object that follows Composite design pattern. I would like to show this object in a WPF using tree view but I have trouble binding the data correctly. I have two classes: Leaf, simple class that doesnt have any children, and Box, compound class that has children elements which could be both of Leaf class of Box class. I also have a common interface called ITree
Interface
public interface ITree
{
string Name { get; }
string Property1 { get; }
string Property2 { get; }
}
Simple class
public class Leaf : ITree
{
string ITree.Name { get { return _name; } }
string ITree.Property1 { get { return property1; } }
string ITree.Property2 { get { return property2; } }
}
Compound class
public class Box : ITree
{
string ITree.Name { get { return _name; } }
string ITree.Property1 { get { return property1; } }
string ITree.Property2 { get { return property2; } }
List<ITree> Children = new List<ITree>();
}
xaml.cs
List<ITree> ListToBind = new List<ITree>();
ITree finalObject = PopulateCompositeObjeectWithData();
ListToBind.Add(finalObject);
xaml
<TreeView ItemsSource="{Binding ElementName=Window, Path= ListToBind}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
The tree view I am trying to achieve:
Box - Name
|-Leaf - Name
|-Leaf - Name
|-Box - Name
| |-Leaf - Name
| |-Leaf - Name
Any suggestion or code samples would be greatly appreciated
Thank you
First of all, Children must be public property for you to be able to bind to it:
public class Box : ITree
{
string ITree.Name { get { return _name; } }
string ITree.Property1 { get { return property1; } }
string ITree.Property2 { get { return property2; } }
public List<ITree> Children { get; } = new List<ITree>();
}
Second, you should bind to explicit implemented interface members using parentheses like this:
<TreeView ItemsSource="{Binding ElementName=Window, Path= ListToBind}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding (local:ITree.Name)}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
This is my first WPF MVVM EF project and while the experience has been very rough and I doubtI'll ever be coming back to these, I'm intent on finishing it. I have a view where you can edit Hardware model's properties. It works just fine for the 'simple' properties such as strings, ints, DateTime and so on. But for some reason I can't make it work with the few FK properties this model has.
Here's the view-viewModel-model code:
<UserControl x:Class="WPFapp.Views.HardwareManipulationWindowView">
<UserControl.Resources>
<DataTemplate DataType="{x:Type localVM:HardwareManipulationViewModel}">
<local:HardwareManipulationWindowView />
</DataTemplate>
</UserControl.Resources>
<ComboBox SelectedValue="{Binding Hardware.CurrentlyBeingUsedByProgram.GUID, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
ItemsSource="{Binding ProgramsList}" SelectedValuePath="GUID">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</UserControl>
internal class HardwareManipulationViewModel : NotificationObject
{
public HardwareManipulationViewModel(Hardware hardware, ObservableCollection<Program> programsList)
{
Hardware = hardware;
ProgramsList = programsList;
}
public ObservableCollection<Program> ProgramsList { get; set; }
public Hardware Hardware { get; }
internal void WriteChangesInto(Hardware selectedItem)
{
selectedItem.Type = Hardware.Type;
selectedItem.Label = Hardware.Label;
selectedItem.Description = Hardware.Description;
selectedItem.Remarks = Hardware.Remarks;
selectedItem.CurrentLocation = Hardware.CurrentLocation;
selectedItem.CurrentStatus = Hardware.CurrentStatus;
//all of the above work just fine, but these 2 FKs below don't work at all
selectedItem.CurrentlyBeingCarriedByPerson = Hardware.CurrentlyBeingCarriedByPerson;
selectedItem.CurrentlyBeingUsedByProgram = Hardware.CurrentlyBeingUsedByProgram;
}
}
public class Hardware : NotificationObject
{
protected Hardware()
{
GUID = Guid.NewGuid();
}
Guid _guid;
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid GUID { get { return _guid; } private set { _guid = value; OnPropertyChanged(); } }
string _label;
public string Label { get { return _label; } set { _label = value; OnPropertyChanged(); } }
string _description;
public string Description { get { return _description; } set { _description = value; OnPropertyChanged(); } }
string _remarks;
public string Remarks { get { return _remarks; } set { _remarks = value; OnPropertyChanged(); } }
Program _currentlyBeingUsedByProgram;
public Program CurrentlyBeingUsedByProgram { get { return _currentlyBeingUsedByProgram; } set { _currentlyBeingUsedByProgram = value; OnPropertyChanged(); } }
}
I've obviously omitted a lot of noise code. For anyone wondering NotificationObject is the basic INotifyPropertyChanged implementation. Now, all of the above is handled inside this single method:
private void InvokeEditHardwareDialog()
{
HardwareManipulationViewModel viewModel = new HardwareManipulationViewModel(SelectedItem.Clone(), new ObservableCollection<Program>(_dbContext.EagerLoad<Program>()));
var window = WindowService.CreateWindowHostingViewModel(viewModel, true);
window.ShowDialog();
if (viewModel.DialogResult.GetValueOrDefault())
{
viewModel.WriteChangesInto(SelectedItem);
_dbContext.Update(SelectedItem);
}
}
Now, the problem is: when the debugger goes into that WriteChangesInto method, all the props above the comment line I've inserted there have their new values as changed using the view, but for the last 2 (foreign key) properties nothing happens, despite the combobox values loading properly. Hardware.CurrentlyBeingUsedByProgram contains whatever the value it started with. What am I doing wrong here? By all my knowledge this should work just fine.
The issue is most likely between EF and WPF communication. I ended up using a workaround, which involves using an additional property to store the FK value:
private Program _hardwareProgram;
public Program HardwareProgram { get { return _hardwareProgram; } set { _hardwareProgram = value; OnPropertyChanged(); } }
And XAML:
<ComboBox SelectedValue="{Binding HardwareProgram}"
ItemsSource="{Binding ProgramsList}" IsSynchronizedWithCurrentItem="True">
Then in your ctor you just read that value _hardwareProgram = hardware.CurrentlyBeingUsedByProgram; and you're good to go.
My Question is i want to see my artist combobox selected artist related albums in my albums combobox.
I found out code example on this site and it is similar to my problem:
but I didn't understand how we get the SelectedArtist value. I pluged this code and test it it keeps giving me null ... I tried to assign artistName to SelectedArtist from the lists of Artits I have but that wasn't successfull:
Can some one help me how i can find the SelectedArtist value before i check if its null or not
I currently have a ComboBox which is populated with Artist names and I need to bind it to another comboBox of albums once the Artist is selected.
These are set up as follows in my view:
XAML
<ComboBox Height="23" HorizontalAlignment="Left" Margin="65,81,0,0" Name="comboBox1" ItemsSource="{Binding Artists}" SelectedItem="{Binding SelectedArtist}" VerticalAlignment="Top" Width="120" />
<ComboBox Height="23" HorizontalAlignment="Left" Margin="65,115,0,0" Name="comboBox2" VerticalAlignment="Top" ItemsSource="{Binding Albums}" SelectedItem="{Binding SelectedAlbums}" Width="120" />
private void initialiseArtists()
{
MusicDataClassesDataContext dataClasses = new MusicDataClassesDataContext();
artistList = (from m in dataClasses.tblArtists select m.ArtistName).ToList();
}
public List<String> Artists
{
get
{
return this.artistList;
}
}
public List<String> Albums
{
set
{
initialiseAlbums();
}
get
{
return this.albumList;
}
}
public string SelectedArtist
{
set
{
this.selectedArtist = value;
initialiseAlbums();
}
}
private void initialiseAlbums()
{
if (selectedArtist != null)
{
MusicDataClassesDataContext dataClasses = new MusicDataClassesDataContext();
var getArtist = dataClasses.tblArtists.FirstOrDefault(band => band.ArtistName== selectedArtist);
albumList = (from album in dataClasses.tblAlbums
where album.ArtistID == getArtist.ArtistID
select album.AlbumName).ToList();
OnPropertyChanged("Albums");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));}
I would do the following:
private List<string> _artists;
public List<string> Artists
{
get { return _artists; }
set
{
_artists = value;
OnPropertyChanged("Artists");
}
}
public List<string> Albums
{
get { return GetAllAlbumsByArtist(SelectedArtist); }
}
private string _selectedArtist;
public string SelectedArtist
{
get { return _selectedArtist; }
set
{
_selectedArtist= value;
OnPropertyChanged("SelectedArtist");
OnPropertyChanged("Albums");
}
}
public List<string> GetAllAlbumsByArtist(string artist)
{
//Write your code to get the album data.
}
Then the only thing you have to do in xaml is to bind the ItensSource property of the two combo boxes correctly and set the UpdateSourceTrigger to "PropertyChanged".
There is a continuation to this question in this thread and marked it as ansawered
The best overloaded method match for 'System.Collections.ObjectModel.ObservableCollection has some invalid arguments
I have a simple listbox in a template file as follows:
<local:ProcessVisualization x:Key="ProcessVisualization"/>
<ListBox Grid.Column="1"
Grid.Row="1"
ItemsSource="{Binding Source={StaticResource ResourceKey=ProcessVisualization}, Path=Instance.TestListItems}"
SelectedItem="{Binding Source={StaticResource ResourceKey=ProcessVisualization}, Path=Instance.SelectedTestListItem, Mode=TwoWay}">
</ListBox>
Then in my ProcessVisualization class I have the following:
private ObservableCollection<string> _testListItems;
private string _selectedTestListItem;
private static readonly ProcessVisualization _processVisualization = new ProcessVisualization();
public ObservableCollection<string> TestListItems
{
get { return _testListItems; }
set
{
_testListItems = value;
NotifyPropertyChanged("TestListItems");
}
}
public string SelectedTestListItem
{
get { return _selectedTestListItem; }
set
{
_selectedTestListItem = value;
NotifyPropertyChanged("SelectedTestListItem");
}
}
public static ProcessVisualization Instance
{
get { return _processVisualization; }
}
When I run methods that assign lists of strings to TestListItems, they show up properly in my listbox, and I can set SelectedTestListItem from code without issue. But if a user tries to pick from the listbox, it doesn't seem to get back to updating my property on the ProcessVisualization class. Anyone know what I did wrong?
Say for example I have the following type:
public class Site
{
public string Name { get; set; }
public int SiteId { get; set; }
public bool IsLocal { get; set; }
}
The above type can be assigned to be held in a Propety in a ViewModel like so assuming a corresponding backing field has been created but omitted here ofc:
public Site SelectedSite
{
get { return _selectedSite; }
set
{
_selectedSite = value;
// raise property changed etc
}
}
In my xaml a straight forward binding would be:
<TextBlock x:Name="StatusMessageTextBlock"
Width="Auto"
Height="Auto"
Style="{StaticResource StatusMessageboxTextStyle}"
Text="{Binding MessageToDisplay,
Mode=OneWay,
UpdateSourceTrigger=PropertyChanged}" />
Can you extend a binding by using the dot notation syntax? e.g:
<TextBlock x:Name="StatusMessageTextBlock"
Width="Auto"
Height="Auto"
Style="{StaticResource StatusMessageboxTextStyle}"
**Text="{Binding SelectedSite.Name,**
Mode=OneWay,
UpdateSourceTrigger=PropertyChanged}" />
Seems like a an interesting feature but my gut instinct is a no as my DC is being assigned at RunTime so at DesignTime or CompileTime, I can't see any clues that could make this feature work or not?
Correct me if have misunderstood what a complex object is, I have simplified mine down for the sake of this question.
Of course this is possible. However, WPF needs to know when any property along the path has changed. To that end, you need to implement INotifyPropertyChanged (or other supported mechanisms). In your example, both Site and the VM containing SelectedSite should implement change notification).
Here's how you could implement the functionality you specified in your question:
// simple DTO
public class Site
{
public string Name { get; set; }
public int SiteId { get; set; }
public bool IsLocal { get; set; }
}
// base class for view models
public abstract class ViewModel
{
// see http://kentb.blogspot.co.uk/2009/04/mvvm-infrastructure-viewmodel.html for an example
}
public class SiteViewModel : ViewModel
{
private readonly Site site;
public SiteViewModel(Site site)
{
this.site = site;
}
// this is what your view binds to
public string Name
{
get { return this.site.Name; }
set
{
if (this.site.Name != value)
{
this.site.Name = value;
this.OnPropertyChanged(() => this.Name);
}
}
}
// other properties
}
public class SitesViewModel : ViewModel
{
private readonly ICollection<SiteViewModel> sites;
private SiteViewModel selectedSite;
public SitesViewModel()
{
this.sites = ...;
}
public ICollection<SiteViewModel> Sites
{
get { return this.sites; }
}
public SiteViewModel SelectedSite
{
get { return this.selectedSite; }
set
{
if (this.selectedSite != value)
{
this.selectedSite = value;
this.OnPropertyChanged(() => this.SelectedSite);
}
}
}
}
And your view might look something like this (assuming a DataContext of type SitesViewModel):
<ListBox ItemsSource="{Binding Sites}" SelectedItem="{Binding SelectedSite}"/>
Below is what worked for me:
public Site SelectedSite
{
get { return _selectedSite; }
set
{
_selectedSite = value;
RaisePropertyChanged("SelectedSite");
}
}
In my xaml I was able to do:
<TextBox Name="tbSiteName"
Width="250"
Height="30"
Margin="0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
IsReadOnly="True"
Style="{StaticResource MainTextBoxStyle}"
Text="{Binding SelectedSite.Name,
Mode=OneWay,
UpdateSourceTrigger=PropertyChanged}" />
This allows you to access data members off the Site Type without having to create individual properties that wrap each data member on the Site Type. Then individual controls can bind to each property declared in the VM. In a one to one fashion, this aproach can become rather verbose. The binding extension attached to the Text property of the TextBox control shown above, shows that we are not binding to a simple straight forward property but actually to a custom type. Potentially removing the need to create more public properties.