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.
Related
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).
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
I Have three objects in a combo box when they are selected the items are listed in the List View. The items are added but there is no text inside the rows like the names of the objects. and when I remove the ListView.View from my Xaml the items displays "(Collection)"
//ComboBox Binding
public void BindComboBox()
{
using (DataClassesDataContext DC = new DataClassesDataContext())
{
cbItem.Items.Clear();
foreach (tblProduct R in DC.tblProducts)
cbItem.Items.Add(R);
cbItem.DisplayMemberPath = "ProductName";
}
//Combobox selectionChanged
List<PIDData> items = new List<PIDData> { };
IEnumerable<PIDData> query = items.Where(item => item.PName != null);
lvDataBinding.Items.Add(items);
txt1.Text = query.ToString();
//Xaml
<ListView Margin="338,20,1,16" Name="lvDataBinding">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding PName}">
<GridViewColumnHeader Tag="Product Name" Width="100">Product Name</GridViewColumnHeader>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<ComboBox x:Name="cbItem" HorizontalAlignment="Left" Margin="13,315,0,0" VerticalAlignment="Top" Width="321" SelectionChanged="cbItem_SelectionChanged"/>
Well what you are doing is that you are adding the LINQ Query result as "one object" to your list view. So the list view is not dealing with each piddata but with one "IEnumable".
So either you are adding them one by one or you change to assignment of ItemsSource.
C# WPF
I have a list view with 3 columns in it. What I want to happen is when I double click on a row, it will capture the values of the columns for the selected row and store them as variables so I can pass them off to another page or simply manipulate them as needed. What I have here is my practice code. For right now, I am just populating a message box to see if I can get the variables out correctly. What is happening is it is not grabbing anything from the row. I figured this out by removing the if block and saw that string name = selectedObject.ilName; was null. an additional question I have is in regards to the statement ingListLV.SelectedItems[0] as InventoryList the [0], what exactly does that refer to? I initally thought it referred to the values it returned, so [0] would be the value in column 1, [1] would be column 2 etc. but i know that is wrong.
Here is my XAML
<ListView x:Name="ingListLV" HorizontalAlignment="Center" Height="100" Margin="0,145,0,0" VerticalAlignment="Top" Width="Auto"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
SelectedValuePath="InventoryName"
Style="{DynamicResource listViewStyle}"
MouseDoubleClick="Open_Edit">
<ListView.View>
<GridView>
<GridViewColumn x:Name="InvName" Width="100" Header="Name" DisplayMemberBinding="{Binding Path=InventoryName}" />
<GridViewColumn Width="50" Header="Qty" DisplayMemberBinding="{Binding Path=Qty}" />
<GridViewColumn Width="50" Header="Type" DisplayMemberBinding="{Binding Path=Type}" />
</GridView>
</ListView.View>
</ListView>
and my code behind
private void Open_Edit(object sender, RoutedEventArgs e)
{
var selectedObject = ingListLV.SelectedItems[0] as InventoryList;
if (selectedObject == null)
{
return;
}
string name = selectedObject.ilName;
MessageBox.Show(name);
}
public class InventoryList
{
public string ilName { get; set; }
public decimal ilQty { get; set; }
public string ilType { get; set; }
}
EDIT
Here is where i am loading data in to the listview
private void LoadLV()
{
auroraDataEntities = new AuroraDataEntities();
ObjectQuery<Inventory> inventories = auroraDataEntities.Inventories;
//Returns only opjects with a quantity greater than 0, so it won't show anything you are out of
var fillList = from q in inventories
where q.Qty > 0
select q;
ingListLV.ItemsSource = fillList.ToList();
}
In ListView.SelectionMode Single (which is default) use SelectedItem not SelectedItems.
var selectedObject = ingListLV.SelectedItem as Inventory;
The [0] refers to the first selected item (row) in a multiple selection.
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}" />