Bind button click to set Icollection view source - c#

When my aapp launched i read a pricing file method attached to the soource using viewm-model approach then display it to the datagrid.
I'm trying to read the pricing file when i click on a button then attach the method to the view source.
I tried to set the view source on button click but nothing is displayed in the datagrid? thanks for your help.
case "ReadPricing": //to read local pricing file
cvs.Source = GetProductsPriceListXML(); // method to read the xml file
cvs.View.Filter = Filter;
View = cvs.View;
return;
Button:
<Button x:Name="breadxml" HorizontalAlignment="Center" Margin="62,10" Width="76" Command="{Binding MyCommand}" CommandParameter="ReadPricing" Height="43" >
<TextBlock Text="Read Pricing File" TextWrapping="Wrap" TextAlignment="Center"/>
</Button>
Datagrid:
<DataGrid VerticalAlignment="Top" HorizontalAlignment="Left"
SelectedItem="{Binding SelectedProduct}"
ItemsSource="{Binding View}" AutoGenerateColumns="False"
CanUserAddRows="False" ScrollViewer.VerticalScrollBarVisibility="Visible"
Margin="0,2,0,0"
Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type DockPanel}}, Path=ActualHeight}" >
<DataGrid.Columns>
<DataGridTextColumn Header="MianProduct" Binding="{Binding Mainproduct}" Width="*" IsReadOnly="True"/>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="*" IsReadOnly="True"/>
<DataGridTextColumn Header="Price" Binding="{Binding Price}" Width="*" />
<!--<DataGridTextColumn Header="Visible" Binding="{Binding Visible}" Width="*" />-->
<DataGridTemplateColumn Header="Visible" Width="100" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Visible, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="NameIcon" Binding="{Binding NameIcon}" Width="*" />
</DataGrid.Columns>
</DataGrid>
Datacontext/convertor:
<Window.DataContext>
<local:ProductPriceViewModel/>
</Window.DataContext>
<Window.Resources>
<local:MediaConverter x:Key="mconv" />
</Window.Resources>
Class-ViewModel:
class ProductPriceViewModel : INotifyPropertyChanged
{
public ProductPriceViewModel() { }
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler? CanExecuteChanged;
public void OnPropertyChanged([CallerMemberName] String info = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
private bool Filter(object item)
{
Product p = item as Product;
if (p == null) return true;
var ret = true;
if (!String.IsNullOrEmpty(MainProductSearch))
ret = ret && p.Mainproduct.IndexOf(MainProductSearch, StringComparison.OrdinalIgnoreCase) >= 0 ||
p.Name.IndexOf(MainProductSearch, StringComparison.OrdinalIgnoreCase) >= 0;
return ret;
}
private CollectionViewSource cvs = new CollectionViewSource();
public ICollectionView View { get; set; }
public ICommand MyCommand { get => new RelayCommand(executemethod, canexecutemethod); }
string pricinglocalfile = #"C:\xmltest\Prices.txt";
private void executemethod(object parameter)
{
switch (parameter.ToString())
{
case "ReadPricing": //to read local pricing file
cvs.Source = GetProductsPriceListXML();
return;
case "SaveFile":
data.Save("xxx.txt");
return;
default:
MediaType = parameter.ToString();
break;
}
View.Refresh();
}
private static bool canexecutemethod(object obj) => true;
XElement data;
private List<Product> GetProductsPriceListXML()
{
var mylist = new List<Product>();
data = XElement.Load(pricinglocalfile);
foreach (XElement xe1 in data.Elements())
if (xe1.Name == "Products")
foreach (var xe2 in xe1.Elements()) mylist.Add(new Product(xe2));
return mylist;
}
}

Although I've not used it myself, reading through Microsoft documentation leads me to believe that CollectionView is actually a control that you can extend that allows you to manipulate a data collection within the UI without actually changing the data itself. It also doesn't appear to be used with DataGrid types but instead replaces them, specifically it expands upon or replaces ListView.
In this regard you need to bind your ItemSource to an actual data list e.g., ObservableCollection<Product>, whether you use a DataGrid type or a CollectionView type within your UI.
Thus I would recommend that you change the following:
private CollectionViewSource cvs = new CollectionViewSource();
public ICollectionView View { get; set; }
to
public ObservableCollection<Product> ProductList {get;set;}
This will automatically trigger a NotifyCollectionChanged when updated.
Then change :
case "ReadPricing": //to read local pricing file
cvs.Source = GetProductsPriceListXML();
return;
to
case "ReadPricing": //to read local pricing file
ProductList.Clear();
var list = GetProductsPriceListXML();
foreach(var p in list)
{
ProductList.Add(p);
}
return;
or
case "ReadPricing": //to read local pricing file
ProductList = new ObservableCollection<Product>(GetProductsPriceListXML());
NotifyPropertyChanged("ProductList");
return;
Then in your View
ItemsSource="{Binding View}" AutoGenerateColumns="False"
becomes
ItemsSource="{Binding ProductList}" AutoGenerateColumns="False"
I believe that if you then wished to manipulate the layout or content of your ProductList at run time you would need to replace your DataGrid with a CollectionView, bind it's ItemSource to ProductList, finally creating a ControlView that presents a CollectionView type property and manipulates the presentation of the data - examples to be found here

Related

Progress bar for parallel file upload with SSH.NET and WPF MVVM

I am trying to build a WPF application that uses SSH.NET to upload multiple files on a server in parallel and display a progress bar for each of them.
In the view I have the following data grid:
<DataGrid
AlternatingRowBackground="LightGray"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
IsReadOnly="True"
ItemsSource="{Binding Files, Mode=TwoWay}"
SelectedItem="{Binding SelectedFile, Mode=OneWayToSource}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=DisplayName}" Header="File" />
<DataGridTextColumn Binding="{Binding Path=FileSize}" Header="Size" />
<DataGridTextColumn Binding="{Binding Path=IsUploaded, Mode=OneWay}" Header="Uploaded" />
<DataGridTextColumn Binding="{Binding Path=IsUploading, Mode=OneWay}" Header="Uploading" />
<DataGridTemplateColumn Header="Progress">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<ProgressBar
Width="300"
Height="10"
Maximum="{Binding FileSize}"
Value="{Binding Path=SizeUploaded, Mode=OneWay}" />
<TextBlock
Margin="0,5,0,0"
HorizontalAlignment="Center"
Text="{Binding Path=PercentageCompleted, StringFormat={}{0}%}" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Start">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button cal:Message.Attach="UploadFile" Content="Start" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Stop">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button cal:Message.Attach="StopTransfer" Content="Stop" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Remove">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button cal:Message.Attach="RemoveFile" Content="Remove" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
The ViewModel has public properties to bind DataGrid's items and SelectedItem:
public BindingList<UploadedFile> Files { get; set; }
public UploadedFile SelectedFile
{
get { return _selectedFile; }
set
{
_selectedFile = value;
NotifyOfPropertyChange(() => SelectedFile);
}
}
To get a file from hard-drive I am using MvvmDialgos library and call this method to create a new UploadedFile and add it to Files list:
public void SelectFile()
{
var success = OpenFileDialog(this);
if (success)
{
var file = new UploadedFile
{
FullName = OpenFileDialogSettings.FileName
};
file.DisplayName = Path.GetFileName(file.FullName);
var fileInfo = new FileInfo(file.FullName);
file.FileSize = (int)fileInfo.Length;
if (Files == null)
Files = new BindingList<UploadedFile>();
Files.Add(file);
NotifyOfPropertyChange(() => Files);
}
}
To upload the file on the server, with SSH.NET I am doing to following:
private async Task UploadFileToServer(ConnectionModel connection, string fileName, string destinationPath)
{
if (!string.IsNullOrWhiteSpace(destinationPath))
{
await Task.Run(() =>
{
var currentUploadedFile = Files.FirstOrDefault(x => x.FullName == fileName);
currentUploadedFile.IsUploading = true;
try
{
_fileManager.UploadFile(connection, fileName, destinationPath);
currentUploadedFile.IsUploaded = true;
}
catch (Exception ex)
{
BroadCastErrorMessage(ex.Message);
IsConnectionTabExpanded = true;
}
finally
{
currentUploadedFile.IsUploading = false;
}
});
}
else
{
BroadCastErrorMessage("ERROR: You must enter a destination folder!");
IsConnectionTabExpanded = true;
}
}
FileManager call's the actually ssh.net library with:
using (var fs = new FileStream(fileName, FileMode.Open))
{
fullFileName = fileName;
sftp.BufferSize = 4 * 1024;
sftp.UploadFile(fs, Path.GetFileName(fileName), true, uploadCallback: UpdateProgresBar);
OnFileUploadedSuccess($#"Successfully uploaded {fileName} in {path}.");
status = FileRenamedStatus.Uploaded;
}
and raises an event with the uploaded size:
private void UpdateProgresBar(ulong uploaded)
{
OnFileUploading(uploaded);
}
protected void OnFileUploading(ulong uploaded)
{
FileUploading?.Invoke(this, new UploadingEventArgs(new UploadedFile
{
SizeUploaded = (int)uploaded,
FullName = fullFileName
}));
}
In the ViewModel I am listening for the event and update the progress bar:
private void OnUploading(object sender, UploadingEventArgs e)
{
foreach (var item in Files.ToList())
{
if (item.FullName == e.UploadedFile.FullName)
{
item.SizeUploaded = e.UploadedFile.SizeUploaded;
var percentage = (double)e.UploadedFile.SizeUploaded / item.FileSize;
item.PercentageCompleted = Math.Round(percentage * 100, 2);
}
}
}
My problem is when I am starting a second file transfer the progress bar for the first one stops and the one of the second transfer is going nuts, displaying random values, increasing and decreasing. My guess is somehow adding in the progress of the first file too.
Somehow I am unable to separate the progress for each progress bar.
What am I doing wrong?
I am not sure if this is the correct fix but it seems that getting a new instance of _fileManager from IoC (using Caliburn.Micro) every time I am calling UploadFileToServer() solved the progressbar issue:
var manager = IoC.Get<IFileTransferManager>();
manager.OnUploading += OnUploading;

WPF MVVM DataGrid Row to Another DataGrid

In my WPF application using MVVM, I have one DataGrid (dgSelectCase) filled with a bound ObservableCollection from my ViewModel. A Button appears in the first column that, when clicked, should add the selected row to the second DataGrid (dgCaseSelected).
The View DataContext is bound to the ViewModel in its code behind, and I know it works because the other Controls on the page (Comboboxes, Textboxes, etc.) work perfectly. The "Add" Button Command in dgSelectCase is bound to the AddTo method in the ViewModel and the "Remove" Button Command in dgCaseSelected is bound to the RemoveFrom method in the ViewModel.
The "Add" Button is not working, but, more importantly, it looks like I have selection Binding issues in the dgSelectCase DataGrid because I get a red box around the DataGrid when I click on a row. Where have I gone wrong? Thanks for any and all help, as I'm still learning.... slowly :). Following are Code Snippets.
XAML
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Select">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Add" Command="{Binding AddTo}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Fac ID #" Binding="{Binding FacilityIDNum}"/>
<DataGridTextColumn Header="OP Case #" Binding="{Binding CaseBookingNum}"/>
<DataGridTextColumn Header="Booking Type #" Binding="{Binding BookingTypeNum}"/>
<DataGridTextColumn Header="Booking Type" Binding="{Binding BookingType}"/>
</DataGrid.Columns>
</DataGrid>
<DataGrid x:Name="dgCaseSelected"
AutoGenerateColumns="False"
ItemsSource="{Binding DGCaseBookingsSelected}"
>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Select">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Remove" Command="{Binding RemoveFrom}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Fac ID #" Binding="{Binding FacilityIDNum}"/>
<DataGridTextColumn Header="OP Case #" Binding="{Binding CaseBookingNum}"/>
<DataGridTextColumn Header="Booking Type #" Binding="{Binding BookingTypeNum}"/>
<DataGridTextColumn Header="Booking Type" Binding="{Binding BookingType}"/>
</DataGrid.Columns>
</DataGrid>
VIEWMODEL
private ObservableCollection<DGCaseBookings> _dgCaseBookingsList;
private ObservableCollection<DGCaseBookings> _dgSelectedCaseBookings;
private ObservableCollection<DGCaseBookings> _dgCaseBookingsSelected;
public ObservableCollection<DGCaseBookings> DGCaseBookingsList
{
get { return _dgCaseBookingsList; }
set
{
SetProperty(ref _dgCaseBookingsList, value, () => DGCaseBookingsList);
}
}
public ObservableCollection<DGCaseBookings> DGSelectedCaseBookings
{
get { return _dgSelectedCaseBookings; }
set
{
SetProperty(ref _dgSelectedCaseBookings, value, () => DGSelectedCaseBookings);
}
}
public ObservableCollection<DGCaseBookings> DGCaseBookingsSelected
{
get { return _dgCaseBookingsSelected; }
set
{
SetProperty(ref _dgCaseBookingsSelected, value, () => DGCaseBookingsSelected);
}
}
public CMBookingSelectVM() : base()
{
DGCaseBookingsList = new ObservableCollection<DGCaseBookings>();
DGCaseBookingsSelected = new ObservableCollection<DGCaseBookings>();
}
private void fillDGCaseBookingswithFacility()
{
using (MySqlConnection con = new MySqlConnection(dbConnectionString))
{
DGCaseBookingsList = new ObservableCollection<DGCaseBookings>();
con.Open();
string Query = ///SELECT STATEMENT WORKS FINE///
MySqlCommand createCommand = new MySqlCommand(Query, con);
MySqlDataReader dr = createCommand.ExecuteReader();
int count = 1;
while (dr.Read())
{
int FacilityIDNum = dr.GetInt32(0);
int CaseBookingNum = dr.GetInt32(1);
int BookingTypeNum = dr.GetInt32(2);
string BookingType = dr.GetString(3);
DGCaseBookings dgcb = new DGCaseBookings(count, FacilityIDNum, CaseBookingNum, BookingTypeNum, BookingType);
DGCaseBookingsList.Add(dgcb);
count++;
}
con.Close();
}
}
public void AddTo(DGCaseBookings dgcb)
{
if (dgcb != null)
{
DGCaseBookingsSelected.Add(dgcb);
DGCaseBookingsList.Remove(dgcb);
}
}
public void RemoveFrom(DGCaseBookings dgcb)
{
if (dgcb != null)
{
DGCaseBookingsList.Add(dgcb);
DGCaseBookingsSelected.Remove(dgcb);
}
}
First of all, you didn't mentioned in your code how you are trying to bind the selected items property of the datagrid to your collection (as a matter of facts you are actually didn't even set the itemsource for the first datagrid), and i think you didn't do that since you actually can't bind the SelectedItems property of the datagrid since it's a readonly property. From now on you have only 2 choice. Find a workaround to send the selected items to your viewmodel, or work on a single row. As for the second approach you can simply do this:
<DataGrid ItemsSource="{Binding DGCaseBookingsList}" SelectedItem="{Binding MyItem}" SelectionMode = "Single" SelectionUnit="FullRow">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Select">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Add" Command="{Binding AddTo}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Fac ID #" Binding="{Binding FacilityIDNum}"/>
<DataGridTextColumn Header="OP Case #" Binding="{Binding CaseBookingNum}"/>
<DataGridTextColumn Header="Booking Type #" Binding="{Binding BookingTypeNum}"/>
<DataGridTextColumn Header="Booking Type" Binding="{Binding BookingType}"/>
</DataGrid.Columns>
</DataGrid>
in your VM delete DGSelectedCaseBookings and add the following:
private DGCaseBookings myItem = new DGCaseBookings();
public DGCaseBookings MyItem
{
get { return myItem; }
set
{
SetProperty(ref myItem, value, () => MyItem);
}
}
From there you will handle this object using the command of your buttons. Just be sure to implement INotifyPropertyChanged or you won't see any change in your UI.
If you aim to a multiple selection, then the easiest way is to work on the selection changed event of your datagrids. From there you can do something like this.
private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
MyVM vm = this.DataContext as MyVM;
DataGrid s = sender as DataGrid;
vm.DGSelectedCaseBookings = s.SelectedItems;
}
In background of this you can setup the event when you click on button and add the current row (like param of the event). Take the current row select it from the datasource/ObservableCollection and add it in another dataset e.g _dgSelectedCaseBookings.
After that you can refresh the new _dgSelectedCaseBookings on UI.

In MahApps Metro DataGridCheckBoxColumn how can I programmatically return checkbox values? (and fix extra row)

I know this isn't necessarily specific to MahApps Metro DataGridCheckBoxColumns, but I figured that information may help someone answer my question.
I'm trying to do two things with my DataGridCheckBox Column in MahApps Metro
1. I would like to have two separate datagrids, and have the ability to return the value in the second column of whichever row is selected
For example, if I had a datagrid that looked like this:
When someone has the checkbox associated with 2 selected, and the checkbox associated with Red selected, I would like a messagebox to show "2 Red".
my .xaml
<DataGrid Name="numberGrid"
ItemsSource="{Binding Path=numberGrid}"
Grid.Row="0"
AutoGenerateColumns="False"
Width="300"
Height="auto">
<DataGrid.Columns>
<DataGridCheckBoxColumn ElementStyle="{DynamicResource MetroDataGridCheckBox}"
EditingElementStyle="{DynamicResource MetroDataGridCheckBox}"
Header="Select"
Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Path=IsSelected, Mode=OneWay}"
/>
<DataGridTextColumn Header="Numbers"
Binding="{Binding Number, Mode=OneWay}"
Width="*"/>
</DataGrid.Columns>
</DataGrid>
<DataGrid Name="colorGrid"
ItemsSource="{Binding Path=colorGrid}"
Grid.Row="0"
AutoGenerateColumns="False"
Width="300">
<DataGrid.Columns>
<DataGridCheckBoxColumn ElementStyle="{DynamicResource MetroDataGridCheckBox}"
EditingElementStyle="{DynamicResource MetroDataGridCheckBox}"
Header="Select"
Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Path=IsSelected, Mode=OneWay}"
/>
<DataGridTextColumn Header="Colors"
Binding="{Binding Color, Mode=OneWay}"
Width="*"/>
</DataGrid.Columns>
</DataGrid>
my .cs with the pseudocode of what I think should happen commented out.
ObservableCollection<MahAppsNumbers> MAnumbers = new ObservableCollection<MahAppsNumbers>
{
new MahAppsNumbers{Number = "1"},
new MahAppsNumbers{Number = "2"},
new MahAppsNumbers{Number = "3"},
new MahAppsNumbers{Number = "4"}
};
public class MahAppsNumbers
{
public string Number { set; get; }
}
ObservableCollection<MahAppsAccents> MAcolors = new ObservableCollection<MahAppsColors>
{
new MahAppsColors{Color = "Red"},
new MahAppsColors{Color = "Orange"},
new MahAppsColors{Color = "Yellow"},
new MahAppsColors{Color = "Green"}
};
public class MahAppsColors
{
public string Color { set; get; }
}
public myWindow()
{
InitializeComponent();
numberGrid.ItemsSource = MAnumbers;
colorGrid.ItemsSource = MAcolors;
//Something like this maybe? I know this isn't a thing you can do
//string currentNumber = numberGrid.SelectedRow[1]; //to get the second column value
//string currentColor = colorGrid.SelectedRow[1];
//MessageBox.Show(currentNumber + currentColor);
}
2. For bonus points, why do I have an extra row at the end?
I've found TONS of solutions for this, that are all basically the same. But that doesn't solve my problem.
Thanks in advance.
Edit 1
Since I want this even to happen every time a box is selected, I'm trying something like this inside of myWindow()
this.numberGrid.SelectedCellsChanged += new SelectedCellsChangedEventHandler(numberGrid_SelectionChanged);
and adding a function that hopefully someone can point me in the right direction to fixing.
private void numberGrid_SelectionChanged(object sender, SelectedCellsChangedEventArgs e)
{
MessageBox.Show(numberGrid.SelectedItem.ToString());
}
Try Binding like this,
<DataGridCheckBoxColumn Binding="{Binding Path=IsSelected, Mode=TwoWay}" />
Now you have a Property IsSelected add this to your collection which binds your DataGrid. When you check your CheckBox IsSelected property Set will call and set it to True, now in your collection has the IsSelected True for which its checked.

How to access my datagrid control in my viewmodel from view?

Hi guys I want to access my datagrid control in my viewmodel.I know this is the incorrect way but I have to do that:
<DataGrid
Grid.Row="1"
Margin="10,10,0,0"
Height="200"
Width="500"
Grid.ColumnSpan="2"
Name="dg"
HorizontalAlignment="Left"
AutoGenerateColumns="False"
ItemsSource="{Binding SSID}"
>
<DataGrid.Columns>
<DataGridTextColumn Width="100" Header="Network ID" Binding="{Binding _networkID}"></DataGridTextColumn>
<DataGridTextColumn Width="100" Header="SSID" Binding="{Binding _ssid}"></DataGridTextColumn>
<DataGridTextColumn Width="100" Header="VLAN" Binding="{Binding _vlan}"></DataGridTextColumn>
</DataGrid.Columns>
void AddSSIDs()
{
var ssid = new SSIDPropertyClass();
ssid._networkID = SSID.Count + 1;
ssid._ssid = EnteredSSIDAC;
ssid._vlan = VlanSSID;
if (ACSelectedSecurityType=="Static WEP")
{
ssid._authenticationMode = ACSelectedSecurityType;
ssid._authentication = ACStaticWEPSelectedAuthentication;
ssid._staticWEPKeyType = ACStaticWEPSelectedKeyType;
ssid._staticWEPKeyLength = ACStaticWEPSelectedKeyLength;
ssid._staticWEPKey1 = StaticWEPKey1;
ssid._staticWEPKey2 = StaticWEPKey2;
ssid._staticWEPKey3 = StaticWEPKey3;
ssid._staticWEPKey4 = StaticWEPKey4;
SSID.Add(ssid);
}
else if(ACSelectedSecurityType=="WPA/WPA2 Personal")
{
ssid._authenticationMode = ACSelectedSecurityType;
ssid._wpaPersonalKeyAC = WpaACKey;
SSID.Add(ssid);
}
}
I want to display only that columns in Datagrid which are selected in if blocks and else if blocks .If the condition of first if block is satisfies than all the other columns present inother else if blocks should be hidden. Please tell me the way in which I can access datagrid directly in ViewModel or any other way by which I can achieve the same thing.Any help would be highly appreciable.
You Can bind colunm visibility prop to your viewmodel prop:
<DataGridTextColumn Width="100" Header="Network ID" Binding="{Binding _networkID}" Visibility="{Binding NetworkVisibility}"></DataGridTextColumn>
<DataGridTextColumn Width="100" Header="SSID" Binding="{Binding _ssid}" Visibilty="{Binding SSIDVisible, Converter={StaticResource SSIDVisible}}"></DataGridTextColumn>
In ViewModel
public Visibility NetworkVisibility
{
get {
if(condition) return Visibility.Visible;
else return Visibility.Collapsed;
}
}
or you can do your viewmodel props of type bool, then just use BoolToVisibilityConverter in XAML
public bool SSIDVisible
{
get {
if(condition) return true;
else return false;
}
}
And for this props you can use NotifyPropertyChanged (if its supposed to change dynamically) as in Andrew Stephens answer.
You could create properties which contain information about the column selection status, for example a bool value, and bind them to the Visible property of your column. Use a converter to convert from bool to Visibility.
You could expose a couple of boolean properties from your VM, indicating which set of columns to display, then bind the Visibility property of each column to the relevant property. You'll need to use the BooleanToVisibilityConverter to convert the boolean value to a Visibility value (Visible or Collapsed). Something like this:-
<Window.Resources>
<BoolToVisibilityConverter x:Key="boolToVisConv" />
</Window.Resources>
<DataGridTextColumn Visibility="{Binding ShowWep, Converter={StaticResource boolToVisConv}" ... />
<DataGridTextColumn Visibility="{Binding ShowWpa, Converter={StaticResource boolToVisConv}" ... />
Edit (some VM code as requested)
Your VM class should implement INotifyPropertyChanged, and its property setters must raise the PropertyChanged event when the value changes. This ensures that anything in the view bound to a property reacts (e.g. refreshes) when its value changes. A typical example of the INPC interface can be found see here. Based on this code, the ShowWpa property would look something like this:-
public class MyViewModel
{
private bool _showWpa;
public bool ShowWpa
{
get
{
return _showWpa;
}
set
{
if (_showWpa != value)
{
_showWpa = value;
NotifyPropertyChanged("ShowWpa");
}
}
}
//etc..
}
A bad practise, but since you want it to be done that way..
Pass it as a parameter to the ViewModel from the code behind of the view.

Updated Property Not Reflected On Screen

I have a Datagrid as follows
<DataGridTextColumn Header="Amount" CellStyle="{StaticResource RightAlignedCellStyle}" Binding="{Binding AmountToAllocate, UpdateSourceTrigger=LostFocus, StringFormat='{}{0:#,0.00}'}" />
<DataGridTemplateColumn Header="Comment" Width="*" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Comment}" TextWrapping="WrapWithOverflow"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Add This Allocation" Command="ac:PICommands.AddAllocation" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
You will see that the ItemSource of the Grid is the property RenewalRows which is an ObservableCollection as follows:-
public ObservableCollection<Data.Payment.UnitRenewalsRow> RenewalRows
{
get
{
return renewalRows;
}
set
{
renewalRows = value;
OnPropertyChanged("RenewalRows");
}
}
SelectedItem is bound to the SelectedRenewalRow property as follows:-
public Data.Payment.UnitRenewalsRow SelectedRenewalRow
{
get
{
return renewalsRow;
}
set
{
renewalsRow = value;
//FullAllocationAmount();
OnPropertyChanged("SelectedRenewalRow");
}
}
There is a button in the last column which calls a command. The code behind as follows:-
private void Allocate(object sender, ExecutedRoutedEventArgs e)
{
ap.AddAnAllocation();
}
ap is the StaticResource class referred in the DataGrid ItemsSource="{Binding Source={StaticResource AllocatePaymentClass}, Path=RenewalRows}”
The code is as follows:-
public void AddAnAllocation()
{
SelectedRenewalRow.Outstanding = SelectedRenewalRow.Outstanding + SelectedRenewalRow.AmountToAllocate;
Allocation allocation = new Allocation();
allocation.Amount = SelectedRenewalRow.AmountToAllocate;
allocation.PaymentInfo = Payment;
allocation.RenewalInfo = SelectedRenewalRow;
allocation.Propref = PropRef;
allocation.FullAddress = FullAddress;
Allocations.Add(allocation);
Payment.Allocations = Allocations;
//reset
SelectedRenewalRow.AmountToAllocate = 0;
}
My problem is the last line. As soon as the user clicks the button that calls AddAnAllocation() I would like the screen to immediately update the selected row in the DataGrid with the AmountToAllocate Property to show as zero. The property is a field on the items in the RenewalRows property shown above. The screen eventually does update but only after the row has been deselected and then reselected and then sometimes even only if a few seconds have gone by.
Any ideas? If you need any further information or code please feel free to ask.
Im not sure if this is any help but it seems using DataTables + DataRows may have caused issues with other people in the past.
Have a look here
and here

Categories

Resources