Update ListView when updates from another Class - c#

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).

Related

Stop Checkbox event from firing multiple times

So I am having an issue where I have a GridView's ItemsSource bound to a collection of object. I also have a column of Check boxes that can be used to select object the user wishes to remove and all related items. The problem I am having is that when the user selects one item, I get stuck in a loop of the items being continually selected. Does anyone have an idea on how I can stop the programmatic selection of these check boxes from firing the Checked event.
Property in use:
List<MyObject> _localCollection = new List<MyObject>();
List<MyObject> LocalCollection
{
get { return _localCollection; }
set
{
_localCollection = value;
OnPropertyChanged("LocalCollection");
}
}
Loose example of XML code:
<GridView Name="grdItems">
<GridViewColumn>
<GridViewColumn.Header>
<CheckBox/>
</GridViewColumn.Header>
<!--Column Template-->
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Tag="{Binding ObjID}"
IsChecked="{Binding ToRemove, Mode=OneWay}"
Checked="SelectRelative" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
The "SelectRelative" method looks as follows:
private void SelectRelative(object sender, RoutedEventArgs e)
{
Dispatcher.BeginInvoke((Action)(() =>
{
//Get the Object Id we need
int selectedId = Convert.ToInt32(((CheckBox)sender).Tag);
//Get all objects that share this ID
List<MyObjects> objLst = new List<MyObjects>(((IEnumerable<MyObjects>)grdItems.ItemsSource));
//Clear the local collection property of our items used in the items source
LocalCollection.Clear();
//Remove the items source since we are updating it
grdItems.ItemsSource = null;
//Go through each item in the list and if the object id's match select them to remove
foreach(var item in objLst)
{
if(item.ObjId == selectedId)
item.ToRemove = true;
//Add the object to our property
LocalCollection.Add(item);
}
//Re-establish the item source with our new collection
grdItems.ItemsSource = LocalCollection;
}));
}
try this:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="chkboxPaid" Checked="chkboxPaid_Checked" Content="check" Style="{StaticResource MaterialDesignCheckBox}" IsChecked="{Binding Path=IsSelected, Mode=TwoWay,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type DataGridRow}}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>

How to get the column value of the selected row in wpf listview

In my listview, I've three columns, the first column is displayed as text with image and the rest of the columns just text only. The listview is coded as below:
<TabItem x:Name="HistoryTab" Header="History" Style="{StaticResource TabStyle}">
<Grid>
<ListView x:Name="HistoryTabLv" HorizontalAlignment="Left" Height="164" Width="275" VerticalAlignment="Top" SelectionChanged="HistoryTabLv_SelectionChanged" SelectionMode="Single">
<ListView.View>
<GridView>
<GridViewColumn x:Name="TimeColumn" Header="Time" Width="85">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="-5,0,0,0">
<Image x:Name="Img" Height="12" Width="12" Source="{Binding Image}" Stretch="Uniform"/>
<TextBlock Text="{Binding Time}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn x:Name="PhoneNumColumn" Header="Phone Number" Width="85" DisplayMemberBinding="{Binding PhoneNum}" />
<GridViewColumn x:Name="DirectionColumn" Header="Direction" Width="95" DisplayMemberBinding="{Binding Direction}" />
</GridView>
</ListView.View>
</ListView>
</Grid>
</TabItem>
If the action statement is true, the relevant data will be binded to each column as coded below.
private void HistoryTabLv_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (myStatement == true)
{
var uri = new Uri(#"/Resources/time.png", UriKind.Relative);
myImg = new BitmapImage(uri);
DateTime myTime = DateTime.Now;
HistoryTabLv.Items.Insert(0, new { Image = myImg, Time = myTime.ToString("hh:mm:ss tt"), PhoneNum = calledNum,
Direction = "Called out" });
}
}
In winform, if I want to get the second column value of the selected row, it is coded like this: (based on what I've searched)
string secondCol = lv.SelectedItems[0].SubItems[1].Text;
I want to get the second column value of the selected row (in my case is the PhoneNum column), how can I do that in WPF. I tried with the code below but it doesn't work. Please help.
string myText = (string)((DataRowView)HistoryTabLv.SelectedItems[0])["PhoneNum"];
In WPF ListViewItem is just a wrapper for your content object and SelectedItem(s) will be of the same type as item in your source collection so normally you would cast HistoryTabLv.SelectedItem to that type but because, as far as I can see, you use anonymous type it makes it a bit more difficult. I think the easiest way is around your problem is to use dynamic
dynamic selectedItem = HistoryTabLv.SelectedItem;
var phoneNum = selectedItem.PhoneNum;
or
dynamic selectedItem = HistoryTabLv.SelectedItems[0];
var phoneNum = selectedItem.PhoneNum;
EDIT
If you would create class for you item like
public class MyItemClass {
public string Image { get; set; }
public string Time { get; set; }
public string PhoneNum { get; set; }
public string Direction { get; set; }
}
and create your item like
new MyItemClass {
Image = myImg,
Time = myTime.ToString("hh:mm:ss tt"),
PhoneNum = calledNum,
Direction = "Called out"
}
then you could cast SelectedItem(s) to your item class like
var selectedItem = (MyItemType)HistoryTabLv.SelectedItem

retrieve object from binding property wpf

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;
}

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.

data binding in wpf using datacontext

I am trying to work out data binding in C# in a wpf application and I got struck.
I have a program and I need to bind my project settings, which can be changed and are displayed in a UI.
UI:
<ListView Name="lsview">
<ListView.DataContext >
<Binding Source="projpro"/>
</ListView.DataContext>
<ListView.View>
<GridView>
<GridViewColumn>
<TextBox Width="150" Name="projid" Text="{Binding Path=pp.test}" />
</GridViewColumn>
<GridViewColumn>
<TextBox Width="150">wtf from xaml</TextBox>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Code:
public MainWindow()
{
InitializeComponent();
displayImage(true);
projectProperties();
projpro pp = new projpro();
lsview.DataContext = pp;
}
class:
class projpro
{
string a="check1";
public string test
{
get { return a ; }
set
{
a = "check2";
}
}
}
The class projpro needs to be a list of several parameters. I want to know how to display it. I have set path to pp. and I want to display the value of test in the UI. I read some artcles on MSDN but still I am clear how to access the code from xaml.
The values are dynamic in the class.
please throw some light.
Thanks a lot in advance.
cheers
Try with the propertyName on the Cell Binding.
<TextBox Width="150" Name="projid" Text="{Binding Path=test}" />

Categories