retrieve object from binding property wpf - c#

Hello im trying to retrieve an object from a list of binding objects. Im using MVVM style
so i have a class name Channel. Channel has the following properties: string Name, string Label, int Id, int assignedLocation, etc..
I also have a XAML file named ChannelSetup.xaml in which there is a
<ListView x:Name="ListView">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Width="65" Header="Channel #">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox "Loaded="LineComboBox_OnLoaded"....
</GridViewColumn>
in my ChannelSetup.xaml.cs file i have something like this
this.AvailableChannelLines = new ObservableCollection<Channel>();
this.DataContext = this;
this.ListView.ItemsSource = this.AvailableChannelLines;
it does populate my list view correctly and everything is fine..
private void LineComboBox_OnLoaded(object sender, RoutedEventArgs e)
{
//// HERE I NEED TO GET THE CURRENT CHANNEL OBJECT
}
but when the LineComboBox_OnLoaded event gets called i want to be able to know/get the current channel object that's is being binded to. How can this be done or i need to use a different approach, methods or events??

DataContext of comboBox will point to the current channel.
private void LineComboBox_OnLoaded(object sender, RoutedEventArgs e)
{
Channel currentChannel = (sender as ComboBox).DataContext as Channel;
}

Related

Bind a checkbox in a listview to a class variable?

I would like to be able to get and set the state of a checkbox in a listview. I would like to either be able to automatically update MyListItems[row].myCheckedValue when the box is clicked by somehow binding in xaml (I know very little about binding) or to be able to loop through each list item by row and access the checkboxes in C#. I don't know how to approach either. I'm just starting out with WPF.
I Could also use Checked and Unchecked events, but I don't know how to retrieve the row of the list item the checkbox is in.
<ListView Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox x:Name="checkBox" Checked="itsChecked" Unchecked="itsUnchecked"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public List<myListItem> MyListItems;
...
listView.ItemsSource = MyListItems;
...
public class myListItem {
public bool myCheckedValue;
}
private void getCheckedItems() {
//Loop through listview rows and get checkbox state
//???
}
private void itsChecked(object sender, RoutedEventArgs e) {
//How can I get the row this checkbox is in??
}
something like
<GridViewColumn Header="Selected">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="chk" IsChecked="{Binding MyListItemsBoolField}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
assuming that your listView.ItemsSource = MyListItems; will stay
It should be as simple as binding IsChecked property of the CheckBox to a property on the ViewModel (you may need to add a new property if it doesn't already exist).

Update ListView when updates from another Class

I am building simple app where It show all the files of certain format in one listview. I have divided program to class "DataFiles" where I establish FileSystemWatcher if something change there. If something change then I want to update my Listview which is placed in MainWindows.xaml.
So I have my MainWindowViewModel.cs and DataFiles.cs and ListView in MainWindow.xaml. How to update List from DataFiles to MainWindow.xaml?
My DataFiles class if something helps:
public FileSystemWatcher filewatcher;
public string ConfigurationFilesSourcePath;
public ObservableCollection<Files> fileslist { get; protected set; } = new ObservableCollection<Files>();
public void InitializeFiles()
{
// Create a new FileSystemWatcher
filewatcher = new FileSystemWatcher();
// Set filter
filewatcher.Filter = "*.txt";
// Set the path
filewatcher.Path = ConfigurationFilesSourcePath;
// Subscribe to the Created event
filewatcher.Created += new FileSystemEventHandler(FileOnchanged);
filewatcher.Changed += new FileSystemEventHandler(FileOnchanged);
filewatcher.Deleted += new FileSystemEventHandler(FileOnchanged);
filewatcher.Renamed += new RenamedEventHandler(FileOnRenamed);
// Enable the FileSystemWatcher events
filewatcher.EnableRaisingEvents = true;
RefreshFilesList();
}
private void FileOnchanged(object sender, FileSystemEventArgs e)
{
RefreshFilesList();
}
private void FileOnRenamed(object sender, RenamedEventArgs e)
{
RefreshFilesList();
}
public void RefreshFilesList()
{
fileslist.Clear();
//string[] getfiles = Directory.GetFiles(FolderLocation);
DirectoryInfo dir = new DirectoryInfo(ConfigurationFilesSourcePath);
string[] extensions = new[] { ".txt" };
int nof = 0;
foreach (FileInfo file in dir.GetFiles().Where(f => extensions.Contains(f.Extension.ToLower())).ToArray())
{
nof++;
fileslist.Add(new Files()
{
FileId = nof,
FileName = file.Name,
FileChanged = file.LastWriteTime.ToString(),
FileCreated = file.CreationTime.ToString(),
OnlyNameWithoutExtension = Path.GetFileNameWithoutExtension(file.Name)
});
}
NotifyPropertyChanged("fileslist");
}
If you think that I must be pretty new in WPF, You're right :) So sorry if this is stupid question. Please for help.
XAML:
<ListView Name="lvfiles" Grid.Row="4" ItemsSource="{Binding fileslist}" SelectionMode="Single" SelectedItem="{Binding SelectedFiles}" DataContext="{Binding }" Style="{StaticResource ListView}">
<ListView.View>
<GridView x:Name="gridFiles">
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox Tag="{Binding ID}" IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListViewItem}}, Path=IsSelected}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn x:Name="FileId" Header="#" DisplayMemberBinding="{Binding FileId}" Width="Auto"/>
<GridViewColumn x:Name="FileName" Header="{inf:Loc ConfigurationsName}" Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding FileName, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" BorderThickness="0" Style="{StaticResource ListViewTextBoxes}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<!--<GridViewColumn x:Name="FileName" Header="{inf:Loc ConfigurationsName}" DisplayMemberBinding="{Binding FileName}" Width="Auto"/>-->
<GridViewColumn x:Name="FileCreated" Header="{inf:Loc ConfigurationsCreated}" DisplayMemberBinding="{Binding FileCreated}" Width="Auto"/>
<GridViewColumn x:Name="FileChanged" Header="{inf:Loc ConfigurationsChanged}" DisplayMemberBinding="{Binding FileChanged}" Width="Auto"/>
</GridView>
</ListView.View>
</ListView>
In the mainmenu you should assign a handler to PropertyChanged event and inside it you can update the list.
DataDiles.PropertyChanged+= DataFiles_PropertyChanged;
public void DataFiles_PropertyChanged(/*...*/)
{
//Refresh the list
}
For more exact code please edit your question and add the definition of your DataFiles class completely.
I hope it will help.
The binding of ItemsSource to the ObservableCollection automatically synchronizes the listview with the collection for every collection.Add/Remove/Clear method call. So I wouldn't recommend to fully remake the collection for every filewatcher event. (If you had 99 files and 1 file was added, you would get 1 collection changed event for the Clear call and 100 events for the 100 Add calls.) Instead you could try to transfer the changes described by the filewatcher events exactly to collection changes: filewatcher.Created --> collection.Add; filewatcher.Deleted --> collection.Remove; filewatcher.Renamed --> collection.Remove + collection.Add. But if the filewatcher misses a file being created/deleted your list would not realize the change. (In fact I wouldn't trust the filewatcher that far.)
Or (easier) you don't use ObservableCollection but just ArrayList as type of your FilesList property. Than no event is raised during the Clear and Add calls and you trigger the synchronization of the ListView by calling NotifyPropertyChanged("fileslist") once at the end of RefreshFilesList as you already do.
Be sure that the DataFiles class implements INotifyPropertyChanged and that the DataContext of your ListView is the DataFiles instance (ideally located as property in your view model).

Update ListView using Linq to Sql Classes in WPF

Im trying to do a simple program that adds data to the database through linq to sql, what im trying to achieve is when i send the data, the listview automatically update here's the behind-code
private DataClasses1DataContext DC = new DataClasses1DataContext();
private void Window_Loaded(object sender, RoutedEventArgs e) {
ListViewData.ItemsSource = DC.Persons;
}
private void Button_Click(object sender, RoutedEventArgs e) {
Person NewPerson = new Person() { Name = nametxt.Text, Address = addresstxt.Text };
DC.Persons.InsertOnSubmit(NewPerson);
DC.SubmitChanges();
}
XAML code
<ListView Name="ListViewData" HorizontalAlignment="Left" Height="238" Margin="10,72,0,0" VerticalAlignment="Top" Width="321">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="Address" DisplayMemberBinding="{Binding Address}"/>
</GridView>
</ListView.View>
</ListView>
i read about the INotifyPropertyChanging, INotifyPropertyChanged but i think the code generated by Visual Studio when a Linq To Sql Class is added already implements them, but i'm not sure, i just started to use WPF
The ListView will not update itself automatically when the source collection changes unless the source collection implements INotifyCollectionChanged. If the source collection does not implement INotifyCollectionChanged then you will have to manually update the ItemsSource property. In your case, however, I do not think you can simply do this:
DC.SubmitChanges();
ListViewData.ItemsSource = DC.Persons;
I am pretty sure (not positive) that DC.Person will still refer to the same object after you call DC.SubmitChanges(). If you try to re-set the ItemsSource property to the same value it will not trigger a refresh.
If the DC.Persons has relatively few items then you could do this:
DC.SubmitChanges();
ListViewData.ItemsSource = DC.Persons.ToArray();
That will copy Persons to a new array and assign that array to the ListView's ItemsSource property. If you have thousands of items then you may want to find another solution; maybe the CollectionViewSource.

Draw resizable table in WPF

I mean how should I display my datastructure for I can work with it like with table?
I want to have a table with rows and columns can be dynamicaly added and removed, but in the rest is should looks like a table.
Now it's represented like IList>, because I should resize it, as I said before. But now i want to display it in DataGrid, and be able to have rows, coulmns, and work with "Cells". But i cannot bind it properly, it it does not display anything, only an empty cell for every row.
What should I do? Or mby use arrays and resize them after each row/column addition?
Please, advice.
Now I have this one:
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
var storage = new Storage();
storage.Data.Add(new DataRow("Bla-bla")
{
new DataEntity() {Text = "bla-bla", Forces = new[] {ForceEnum.AA}},
new DataEntity() {Text = "bla-bla", Forces = new[] {ForceEnum.UA}}
});
DataListView.DataContext = new StorageModel(storage);
}
public class StorageModel
{
public StorageModel()
{
}
public StorageModel(IStorage storage)
{
DataRowList = new ObservableCollection<DataRow>(storage.Data);
}
public ObservableCollection<DataRow> DataRowList
{
get;
set;
}
}
public class DataRow : IList<DataEntity>
{
public string Name { get; private set; }
private readonly List<DataEntity> _list = new List<DataEntity>();
...
_
<ListView ItemsSource="{Binding DataRowList}" Name="DataListView">
<ListView.ItemTemplate>
<DataTemplate>
<ListView ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
i want be able to create something similar to this, but with 2-way binding...
You seem to be asking for TreeView and not ListView since you have a tree alike data structure.
If you do not want to use a TreeView I would suggest you to use a simple ListView with a small trick. That is you could explose an Expander which will add or remove items underneath parent with an indent to fake tree look and still all cells in column would be resized together.
Btw dont forgot to define columns
Here is how:
<ListView Margin="10" Name="lvUsers">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Age" Width="50" DisplayMemberBinding="{Binding Age}" />
<GridViewColumn Header="Mail" Width="150" DisplayMemberBinding="{Binding Mail}" />
</GridView>
</ListView.View>
</ListView>
If you wish to implement that expander trick behavior I would suggest you to listen to event OnExpanded and add or remove the needed rows.

WPF Binded list is updated from datagrid but I don't want it

I'm new in WPF/C# and I'm in trouble with bindings :(
I've got a ListView in my main Window which is binded from a list.
I've got a second window with a datagrid which is binded from another list (a copy from the first one).
My probleme is when I update datas from the datagrid (2nd window), it upgrades datas from the first window too but I don't want it. (I want the first list to be independant from the other)
Here is the code from the 1st window:
public partial class MainWindow : Window
{
List<Voiture> mesBagnoles = new List<Voiture>();
public MainWindow()
{
InitializeComponent();
...
lstViewBagnoles.ItemsSource = mesBagnoles;
...
}
}
xaml :
<ListView Grid.Row="0" x:Name="lstViewBagnoles" selectionChanged="lstViewBagnoles_SelectionChanged" Width="220">
<ListView.Resources>
<Style TargetType="GridViewColumnHeader">
<Setter Property="Visibility" Value="Collapsed"></Setter>
</Style>
</ListView.Resources>
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn DisplayMemberBinding="{Binding Marque}"/>
<GridViewColumn DisplayMemberBinding="{Binding Modele}"/>
<GridViewColumn DisplayMemberBinding="{Binding Moteur}"/>
</GridView>
</ListView.View>
</ListView>
Openning 2nd window:
private void btnVoitures_Click(object sender, RoutedEventArgs e)
{
VoituresWindow voitureWindow = new VoituresWindow(mesBagnoles);
voitureWindow.ShowDialog();
}
2nd Window :
public partial class VoituresWindow : Window
{
public List<Voiture> listBagnoles = new List<Voiture>();
public VoituresWindow(List<Voiture> e)
{
listBagnoles = e;
InitializeComponent();
dataVoitures.ItemsSource = listBagnoles;
}
}
And xaml :
<DataGrid x:Name="dataVoitures" Grid.Row="0" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Marque" Binding="{Binding Marque}" />
<DataGridTextColumn Header="Modèle" Binding="{Binding Modele}" />
<DataGridTextColumn Header="Moteur" Binding="{Binding Moteur}" />
...
</DataGrid.Columns>
</DataGrid>
As you can see, the binding source of the listview and the datagrid are from 2 differents lists.
I think I miss something with DataContext or else but I cannot find a way
Hope you can help me :)
EDIT :
Ok so I replaced the 2nd window opening :
private void btnVoitures_Click(object sender, RoutedEventArgs e)
{
VoituresWindow voitureWindow = new VoituresWindow(mesBagnoles);
voitureWindow.ShowDialog();
}
By this:
private void btnVoitures_Click(object sender, RoutedEventArgs e)
{
List<Voiture> mesBagnoles2 = new List<Voiture>(mesBagnoles);
VoituresWindow voitureWindow = new VoituresWindow(mesBagnoles2);
voitureWindow.ShowDialog();
}
But the fist window listview is always updated by the 2nd window datagrid :/
I think there is something wrong with my binding
As sa_ddam213 & Mike Schartz say, you pass the same List to the second window, or as i can read you want a copy of this one.
Just initialize another list and affect it with the value of mesBagnoles.

Categories