Duplicate column with different values - c#

I am trying to make a simple download manager, I am having trouble with adding a column with ProgressBars, it creates two columns with the same value but one has
numeric and one has progressbar values. How can I make "Progress1" be hidden or remove it but still keep the values in "Progress2"
Here is the main xaml
<DataGrid.Columns>
<!--#region Program Number -->
<DataGridTemplateColumn SortMemberPath="#" Header="#">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBlock Text="{Binding ProgramID}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<!--#endregion-->
<!--#region Program Number -->
<DataGridTemplateColumn SortMemberPath="Name" Header="Name">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<!--#endregion-->
<!--#region Program Number -->
<DataGridTemplateColumn SortMemberPath="Size" Header="Size">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBlock Text="{Binding Size}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<!--#endregion-->
<!--#region Progress bar columns -->
<DataGridTemplateColumn SortMemberPath="Progress"
Header="Progress" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ProgressBar Value="{Binding Progress}"
Height="15"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!--#endregion-->
</DataGrid.Columns>
</DataGrid>
Here is the Code-Behind of the programs
public class Programs : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnProperyChanged(string PropertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
private int _programid;
public int ProgramID
{
get { return _programid; }
set
{
_programid = value;
OnProperyChanged($"{nameof(ProgramID)}");
}
}
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnProperyChanged($"{nameof(Name)}");
}
}
private double _size;
public double Size
{
get { return _size; }
set
{
_size = value;
OnProperyChanged($"{nameof(Size)}");
}
}
private double _progress;
public double Progress
{
get { return _progress; }
set
{
_progress = value;
OnProperyChanged($"{nameof(Progress)}");
}
}
public ObservableCollection<Programs> ProgramList()
{
ObservableCollection<Programs> programs = new ObservableCollection<Programs>
{
new Programs { _programid = 1, _name = "Program0", _size = 5.1, _progress = 100 },
new Programs { _programid = 2, _name = "Program1", _size = 7.1, _progress = 36.7 },
new Programs { _programid = 3, _name = "Program2", _size = 1.1, _progress = 44.7 },
new Programs { _programid = 4, _name = "Program3", _size = 6.1, _progress = 88.7 },
new Programs { _programid = 5, _name = "Program4", _size = 2.1, _progress = 89.7 },
new Programs { _programid = 6, _name = "Program5", _size = 9.1, _progress = 68.7 },
new Programs { _programid = 7, _name = "Program6", _size = 5.1, _progress = 98.7 },
new Programs { _programid = 8, _name = "Program7", _size = 2.1, _progress = 59.7 },
new Programs { _programid = 9, _name = "Program8", _size = 83.1, _progress = 18.7 }
};
return programs;
}
}
Here is what it look like
(First time posting a question so if anything is missing I'll be glad to add)

The problem is the columns are getting auto generated because you have not set AutoGeneratedColumns="false" and true is the default.
When you don't set it to false the DataGrid will generate a column for each property in the assigned viewmodel.
For more information have a look at DataGrid.AutoGenerateColumns Property
When you set it to false you'll only see the progress bar. So you have to specifiy the other columns like you did it with the ProgressBar.
You could it like this
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding ProgramID}"/>
<DataGridTextColumn Binding="{Binding Name}"/>
<DataGridTextColumn Binding="{Binding Size}"/>
<DataGridTemplateColumn SortMemberPath="Progress" Header="Progress2">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ProgressBar Value="{Binding Progress1}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
edit
Use DataGridTextColumn like this
<DataGridTextColumn SortMemberPath="ProgramID"
Header="#"
Binding="{Binding ProgramID}"/>
instead of a TemplateColumn when you want to show text. Much easier ;)
The problem why you won't see anything with your code is hat you only set the
CellEditingTemplate and not the CellTemplate.
The CellEditingTemplate is the template which will be shown when the cell is in edit mode.
The CellTemplate is the one which will be shown when the cell isn't in edit mode.
When you wan't to do it your way you have to change this like
<DataGridTemplateColumn SortMemberPath="ProgramID" Header="#">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding ProgramID}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Related

Progress bars are not updating

I've got a problem with {Binding CurrentProgress} value of Progressbar control inside of a Listview. Within my code I'm able to add few items which has couple of properties with CurrentProgress property as well. Items are adding in a proper way, yet only ONE progressbar's updating. Here's the code:
MODEL:
sealed public class Mp3Model : INotifyPropertyChanged
{
public string Name { get; set; }
private double _currentProgress;
public double CurrentProgress
{
get
{
return _currentProgress;
}
set
{
_currentProgress = value;
OnPropertyChanged("CurrentProgress");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<ListView ItemsSource="{Binding Mp3List}">
<ListView.View>
<GridView>
<GridViewColumn
Width="140"
DisplayMemberBinding="{Binding Name}"
Header="Track Name" />
<GridViewColumn Width="300" Header="Progress">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid Width="320">
<ProgressBar
Height="40"
Margin="10"
IsIndeterminate="{Binding IsIndeterminate}"
Maximum="100"
Minimum="0"
Visibility="{Binding IsProgressDownloadVisible}"
Value="{Binding CurrentProgress}" />
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding CurrentProgress, StringFormat={}{0:0}%}"
Visibility="{Binding IsPercentLabelVisible}" />
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding ConvertingLabelText}"
Visibility="{Binding IsConvertingLabelVisible}" />
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding IsOperationDone}"
Visibility="{Binding IsOperationDoneLabelVisible}" />
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
VIEW-MODEL (Method)
private void SaveVideoToDisk()
{
Task.Factory.StartNew(() =>
{
long currentLocalProgress = 0;
this._fileHelper = new FileHelper();
this._model = new Mp3Model();
using (var service = Client.For(YouTube.Default))
{
using (var video = service.GetVideo(YoutubeLinkUrl))
{
_fileHelper.DefaultTrackName = video.FullName;
_fileHelper.DefaultTrackPath = _fileHelper.Path + "\\" + _fileHelper.DefaultTrackName;
_fileHelper.DefaultTrackHiddenPath = _fileHelper.HiddenPath + "\\" + _fileHelper.DefaultTrackName;
_fileHelper.TmpTrackPath = _fileHelper.PreparePathForFFmpeg(_fileHelper.DefaultTrackHiddenPath);
_model = new Mp3Model()
{
Name = _fileHelper.DefaultTrackName,
IsProgressDownloadVisible = Visibility.Visible,
IsPercentLabelVisible = Visibility.Visible,
IsConvertingLabelVisible = Visibility.Hidden,
IsOperationDoneLabelVisible = Visibility.Hidden,
ConvertingLabelText = Consts.ConvertingPleaseWait,
CurrentProgress = 0,
};
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
this._mp3List.Add(_model);
}));
using (var outFile = File.OpenWrite(_fileHelper.TmpTrackPath))
{
using (var progressStream = new ProgressStream(outFile))
{
var streamLength = (long)video.StreamLength();
progressStream.BytesMoved += (sender, args) =>
{
currentLocalProgress = args.StreamLength * 100 / streamLength;
_model.CurrentProgress = currentLocalProgress;
Debug.WriteLine($"{_model.CurrentProgress}% of video downloaded");
};
video.Stream().CopyTo(progressStream);
}
}
//ExtractAudioFromVideo(_fileHelper.TmpTrackPath);
}
}
});
}
Place of the ProgressBar Binded value:
progressStream.BytesMoved += (sender, args) =>
{
currentLocalProgress = args.StreamLength * 100 / streamLength;
_model.CurrentProgress = currentLocalProgress;
Debug.WriteLine($"{_model.CurrentProgress}% of video downloaded");
};
Does anyone has any idea?
Trying to guess: you update a _model field which will be overriden each time you call the save video to disk method. This may only work if only one call of this method can be done by instance of the class (but as we don't have the clas, we don't know if it's the list or the video).
So I would say that invoking twice the method stops the first _model instance from being updated (as the lambda captures the variable holding the object)
xaml looks okey to me, but I had similar problems, I fixed as follows:
add Mode=OneWay to the Value="{Binding CurrentProgress, Mode=OneWay}".
Also whenever you update CurrentProgress use the Dispatcher: Application.Current.Dispatcher.Invoke(() => CurrentProgress++);
I hope it can helps you to find the solution.
Edit: Just a suggestion, I use the following OnPropertyChanged, so you don't have to write the name of the properties everytime =D.
protected void OnPropertyChange([CallerMemberName] string inPropertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(inPropertyName));

How to get Binding name from DataTemplate

I'm trying to sort a ListView by it's headers.
I'm following this MSDN example, with the alternation given here - where this line works if I were to use direct binding:
GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader;
string sortString = ((Binding)headerClicked.Column.DisplayMemberBinding).Path.Path
But problem is I'm not binding the columns directly using DisplayMemberBinding="{Binding PVNum}" but rather I am using CellTemplate:
<ListView.Resources>
<DataTemplate x:Key="NumberTemplate">
<TextBlock Text="{Binding PVNum}" TextAlignment="Center" />
</DataTemplate>
</ListView.Resources>
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridView.Columns>
<GridViewColumn Header=" " CellTemplate="{StaticResource NumberTemplate}"/>
</GridView.Columns>
</GridView>
</ListView.View>
So my question is - how do I get this "PVNum" string in the code behind?
I did try this, though s is null - so I guess I'm off:
var t = headerClicked.Column.CellTemplate.LoadContent() as TextBlock;
var s = t.GetBindingExpression(TextBox.TextProperty);
Any suggestions?
It should be TextBlock.Text property:
var t = headerClicked.Column.CellTemplate.LoadContent() as TextBlock;
var s = t.GetBindingExpression(TextBlock.TextProperty);
string sourcePropertyName = s.ParentBinding.Path.Path;
A possible solution is to define an attached property for GridViewColumn:
public class GridViewColumnAttachedProperties
{
public static readonly DependencyProperty SortPropertyNameProperty = DependencyProperty.RegisterAttached(
"SortPropertyName", typeof(string), typeof(GridViewColumnAttachedProperties), new PropertyMetadata(default(string)));
public static void SetSortPropertyName(DependencyObject element, string value)
{
element.SetValue(SortPropertyNameProperty, value);
}
public static string GetSortPropertyName(DependencyObject element)
{
return (string) element.GetValue(SortPropertyNameProperty);
}
}
In XAML you set the attached properties to the Path used in the Binding inside the templates. Based on the example from the MSDN site:
<ListView x:Name='lv' Height="150" HorizontalAlignment="Center" VerticalAlignment="Center" GridViewColumnHeader.Click="GridViewColumnHeaderClickedHandler">
<ListView.Resources>
<DataTemplate x:Key="YearTemplate">
<TextBlock Text="{Binding Year}" TextAlignment="Center" />
</DataTemplate>
<DataTemplate x:Key="MonthTemplate">
<TextBlock Text="{Binding Month}" TextAlignment="Center" />
</DataTemplate>
<DataTemplate x:Key="DayTemplate">
<TextBlock Text="{Binding Day}" TextAlignment="Center" />
</DataTemplate>
</ListView.Resources>
<ListView.ItemsSource>
<collections:ArrayList>
<system:DateTime>1993/1/1 12:22:02</system:DateTime>
<system:DateTime>1993/1/2 13:2:01</system:DateTime>
<system:DateTime>1997/1/3 2:1:6</system:DateTime>
<system:DateTime>1997/1/4 13:6:55</system:DateTime>
<system:DateTime>1999/2/1 12:22:02</system:DateTime>
<system:DateTime>1998/2/2 13:2:01</system:DateTime>
<system:DateTime>2000/2/3 2:1:6</system:DateTime>
<system:DateTime>2002/2/4 13:6:55</system:DateTime>
<system:DateTime>2001/3/1 12:22:02</system:DateTime>
<system:DateTime>2006/3/2 13:2:01</system:DateTime>
<system:DateTime>2004/3/3 2:1:6</system:DateTime>
<system:DateTime>2004/3/4 13:6:55</system:DateTime>
</collections:ArrayList>
</ListView.ItemsSource>
<ListView.View>
<GridView>
<GridViewColumn CellTemplate="{StaticResource YearTemplate}" local:GridViewColumnAttachedProperties.SortPropertyName="Year" />
<GridViewColumn CellTemplate="{StaticResource MonthTemplate}" local:GridViewColumnAttachedProperties.SortPropertyName="Month" />
<GridViewColumn CellTemplate="{StaticResource DayTemplate}" local:GridViewColumnAttachedProperties.SortPropertyName="Day" />
</GridView>
</ListView.View>
</ListView>
And in the Click event handler you can just retrieve the value of the attached property with string bindingName = headerClicked.Column.GetValue(GridViewColumnAttachedProperties.SortPropertyNameProperty) as string;. Based on the MSDN example:
GridViewColumnHeader _lastHeaderClicked;
ListSortDirection _lastDirection = ListSortDirection.Ascending;
private void GridViewColumnHeaderClickedHandler(object sender, RoutedEventArgs e)
{
GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader;
if (headerClicked != null)
{
if (headerClicked.Role != GridViewColumnHeaderRole.Padding)
{
ListSortDirection direction;
if (!ReferenceEquals(headerClicked, _lastHeaderClicked))
{
direction = ListSortDirection.Ascending;
}
else
{
if (_lastDirection == ListSortDirection.Ascending)
{
direction = ListSortDirection.Descending;
}
else
{
direction = ListSortDirection.Ascending;
}
}
string bindingName = headerClicked.Column.GetValue(GridViewColumnAttachedProperties.SortPropertyNameProperty) as string;
Sort(bindingName, direction);
if (direction == ListSortDirection.Ascending)
{
headerClicked.Column.HeaderTemplate = Resources["HeaderTemplateArrowUp"] as DataTemplate;
}
else
{
headerClicked.Column.HeaderTemplate = Resources["HeaderTemplateArrowDown"] as DataTemplate;
}
// Remove arrow from previously sorted header
if (_lastHeaderClicked != null && !ReferenceEquals(_lastHeaderClicked, headerClicked))
{
_lastHeaderClicked.Column.HeaderTemplate = null;
}
_lastHeaderClicked = headerClicked;
_lastDirection = direction;
}
}
}
private void Sort(string sortBy, ListSortDirection direction)
{
ICollectionView dataView = CollectionViewSource.GetDefaultView(lv.ItemsSource);
dataView.SortDescriptions.Clear();
SortDescription sd = new SortDescription(sortBy, direction);
dataView.SortDescriptions.Add(sd);
dataView.Refresh();
}

Sending DataGrid (DataGridRowDetailsEventArgs e) AsyncResult

Good day.
Have a DataGrid (operationGrid) RowDetailsTemplate, which contains the DataGrid (sumOperationGrid)
<DataGrid AutoGenerateColumns="False" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserSortColumns="False" IsReadOnly="True" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}, Path=OperCountUnits}" Name="operationGrid" RowDetailsVisibilityMode="VisibleWhenSelected" SelectionMode="Single" VerticalContentAlignment="Bottom" Grid.ColumnSpan="2" Margin="6,60,7,6" LoadingRowDetails="operationGrid_LoadingRowDetails" SelectionChanged="operationGrid_SelectionChanged">
<DataGrid.Columns>
<DataGridTextColumn x:Name="Id" Binding="{Binding Path=Id}" Header="{Binding NumberOfOperation, Source={StaticResource resources}}" IsReadOnly="True" />
<DataGridTextColumn x:Name="StartedAt" Binding="{Binding Path=StartedAt, Converter={StaticResource ConvertCultureDate}}" Header="{Binding StartedAt, Source={StaticResource resources}}" IsReadOnly="True" />
<DataGridTextColumn x:Name="FinisedAt" Binding="{Binding Path=FinishedAt, Converter={StaticResource ConvertCultureDate}}" Header="{Binding FinishedAt, Source={StaticResource resources}}" IsReadOnly="True" />
<DataGridTextColumn x:Name="CountMode" Binding="{Binding Path=Description}" Header="{Binding CountMode, Source={StaticResource resources}}" IsReadOnly="True" />
<DataGridTextColumn x:Name="CurrencyCode" Header="{Binding CurrcencyCode, Source={StaticResource resources}}" IsReadOnly="True" />
<DataGridTextColumn x:Name="UnitId" Binding="{Binding Path=UnitId}" Header="UnitId" IsReadOnly="True" Visibility="Collapsed" />
<DataGridTextColumn x:Name="CountModeId" Binding="{Binding Path=CountModeId}" Header="CountModeId" IsReadOnly="True" Visibility="Collapsed" />
<DataGridTextColumn x:Name="HotListCount" Binding="{Binding Path=HotListCount}" Header="Фальш" IsReadOnly="True" Visibility="Collapsed" />
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid AutoGenerateColumns="False" IsReadOnly="True" ItemsSource="{Binding CurrencySum}" Name="sumOperationGrid" SelectionChanged="sumOperationGrid_SelectionChanged" SelectionMode="Single">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding csId}" Visibility="Collapsed" />
<DataGridTextColumn Binding="{Binding CurrId}" Visibility="Collapsed" />
<DataGridTextColumn Binding="{Binding Curr}" Header="{Binding Currency, Source={StaticResource resources}}" />
<DataGridTextColumn Binding="{Binding TotalSum, Converter={StaticResource replaceNull}}" Header="{Binding Sum, Source={StaticResource resources}}" />
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
The request is for the WCF service and it turns out the answer is everything is good
If you submit data through a delegate, then the answer is in the datagrid (sumOperationGrid) come and displays.
private delegate void CurrencySumByCurrentItemInvoke(DataGrid sumGrid,List<CurrencySum> sum );
private void operationGrid_LoadingRowDetails(object sender, DataGridRowDetailsEventArgs e)
{
OperCountUnit _con = (OperCountUnit)e.Row.DataContext;
DataGrid s2 = e.DetailsElement.FindName("sumOperationGrid") as DataGrid;
client.BeginGetCurrencySumByCurrentItem((int)_con.Id,
new AsyncCallback(
delegate(IAsyncResult result)
{
List<CurrencySum> sum = client.EndGetCurrencySumByCurrentItem(result).ToList<CurrencySum>();
if (sum != null)
{
s2.Dispatcher.BeginInvoke(new CurrencySumByCurrentItemInvoke(GetCurrencySumByCurrentItemInvoke), new object[2] {s2, sum});
}
}
), null);
}
void GetCurrencySumByCurrentItemInvoke(DataGrid sumGrid, List<CurrencySum> sum)
{
sumGrid.ItemsSource = sum;
}
Tried so.
private void operationGrid_LoadingRowDetails(object sender, DataGridRowDetailsEventArgs e)
{
OperCountUnit _con = (OperCountUnit)e.Row.DataContext;
DataGrid s2 = e.DetailsElement.FindName("sumOperationGrid") as DataGrid;
client.BeginGetCurrencySumByCurrentitem((int)_con.Id, new AsyncCallback (GetCurrencySumByCurrentItemCallBack), null);
}
private delegate void CurrencySumByCurrentItemInvoke(DataGrid sumGrid,List<CurrencySum> sum );
private void GetCurrencySumByCurrentItemCallBack(IAsyncResult result)
{
DataGridRow opGrRow = (DataGridRow)(this.operationGrid.ItemContainergenerator.containerfromitem (this.operationGrid.Items.Currentitem));
ContentPresenter cpOpGrRow = FindVisualChild<ContentPresenter>(opGrRow);
DataTemplate dtOpGrRow = cpOpGrRow.ContentTemplate;
DataGrid s2 = (DataGrid)dtOpGrRow.FindName("sumOperationGrid", cpOpGrRow);
}
void GetCurrencySumByCurrentItemInvoke(DataGrid sumGrid, List<CurrencySum> sum)
{
sumGrid.ItemsSource = sum;
}
private childItem FindVisualChild<childItem>(DependencyObject obj)
where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
return (childItem)child;
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
and so
private void operationGrid_LoadingRowDetails(object sender, DataGridRowDetailsEventArgs e)
{
OperCountUnit _con = (OperCountUnit)e.Row.DataContext;
DataGrid s2 = e.DetailsElement.FindName("sumOperationGrid") as DataGrid;
client.BeginGetCurrencySumByCurrentitem((int)_con.Id, new AsyncCallback (GetCurrencySumByCurrentItemCallBack), s2);
}
private delegate void CurrencySumByCurrentItemInvoke(DataGrid sumGrid,List<CurrencySum> sum );
private void GetCurrencySumByCurrentItemCallBack(IAsyncResult result)
{
DataGrid s2 = result.AsyncState as DataGrid
List<CurrencySum> sum = client.EndGetCurrencySumByCurrentitem(result).ToList<CurrencySum>();
if (sum != null)
{
s2.Dispatcher.BeginInvoke(new CurrencySumByCurrentItemInvoke(GetCurrencySumByCurrentItemInvoke), new object[2] {s2, sum});
}
}
void GetCurrencySumByCurrentItemInvoke(DataGrid sumGrid, List<CurrencySum> sum)
{
sumGrid.ItemsSource = sum;
}
In both cases, it all boils down to the error
"The calling thread cannot access this object because the owner of this object is another thread."
The question of how to remove a delegate from client.BeginGetCurrencySumByCurrentitem and forward data table s2 in a separate function?
How do I get back into your flow and load data into a table sumOperationGrid ?
If I understood messy code I cant understand why you trying to pass through data grid everywhere. Only try to access grid and set ItemsSource at the end of operation only by passing sum. Here is pseudocode of callback function:
private void GetCurrencySumByCurrentItemCallBack(IAsyncResult result)
{
if(sum != null)
{
this.Invoke(new Action(()=>
{
//get data grid
//set ItemSource
})
}
}
Or if you do need to have data grid to Invoke try to do something like this:
DataGrid myDataGrid;
private void operationGrid_LoadingRowDetails(object sender, DataGridRowDetailsEventArgs e)
{
OperCountUnit _con = (OperCountUnit)e.Row.DataContext;
myDataGrid = e.DetailsElement.FindName("sumOperationGrid") as DataGrid;
//do stuff without passing grid
}
private void GetCurrencySumByCurrentItemCallBack(IAsyncResult result)
{
List<CurrencySum> sum = client.EndGetCurrencySumByCurrentitem(result).ToList<CurrencySum>();
if (sum != null)
{
myDataGrid.Dispatcher.BeginInvoke(new Action(()=>
{
myDataGrid.ItemSource = sum;
});
}
Yea. This a good idea. Thank a lot!
private void GetCurrencySumByCurrentItemCallBack(IAsyncResult result)
{
if(sum != null)
{
this.Invoke(new Action(()=>
{
//get data grid
//set ItemSource
})
}
}
I changed my code
private void operationGrid_LoadingRowDetails(object sender, DataGridRowDetailsEventArgs e)
{
OperCountUnit _con = (OperCountUnit)e.Row.DataContext;
client.BeginGetCurrencySumByCurrentItem((int)_con.Id, new AsyncCallback(GetCurrencySumByCurrentItemCallBack), null);
}
private delegate void CurrencySumByCurrentItemInvoke(List<CurrencySum> sum);
private void GetCurrencySumByCurrentItemCallBack(IAsyncResult result)
{
List<CurrencySum> sum = client.EndGetCurrencySumByCurrentItem(result).ToList<CurrencySum>();
if (sum != null)
{
Dispatcher.Invoke(new CurrencySumByCurrentItemInvoke(GetCurrencySumByCurrentItemInvoke), new object[1] { sum });
}
}
void GetCurrencySumByCurrentItemInvoke(List<CurrencySum> sum)
{
DataGridRow opGrRow = (DataGridRow)(this.operationGrid.ItemContainerGenerator.ContainerFromItem(this.operationGrid.SelectedItem));
DataGridDetailsPresenter cpOpGrRow = FindVisualChild<DataGridDetailsPresenter>(opGrRow);
DataTemplate dtOpGrRow = cpOpGrRow.ContentTemplate;
DataGrid s2 = (DataGrid)dtOpGrRow.FindName("sumOperationGrid", cpOpGrRow);
s2.ItemsSource = sum;
}

mvvm:listview selected item fired when ItemsSource get value

I am new to MVVM I have scenario I have One textbox and One Listview .When textbox text(PONo) changed , the listview get populated with some data (PORecords) by filtering through the text.
My View is:
<TextBox Height="24"
VerticalAlignment="Top"
Width="119"
Text="{Binding Path=PONo,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
/>
<ListView Height="161"
ItemsSource="{Binding Path=PORecords}"
SelectedItem="{Binding Path=SelectedPO,Mode=TwoWay}"
IsSynchronizedWithCurrentItem="True"
Visibility="{Binding Path=PORecords,Converter={StaticResource ResourceKey=NullToVisibilityConverter}}"
Width="357">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Path=PurchaseOrderRefNo}"
Header="Order No"
Width="100" />
<GridViewColumn DisplayMemberBinding="{Binding Path=OrderDate, StringFormat=dd-MMM-yyyy}"
Header="Order Date"
Width="100" />
</GridView>
</ListView.View>
</ListView>
My ViewModel is:
private string _pONo;
public string PONo
{
get { return _pONo; }
set
{
if (value != "" && value!=null)
{
_pONo = value;
List<Tbl_PurchaseOrderMain> _lst = new List<Tbl_PurchaseOrderMain>();
_lst = new Tbl_PurchaseOrderMain().Select(" PurchaseOrderRefNo like '" + _pONo + "%'");
if (_lst.Count != 0)
{
PORecords = _lst;
}
else
{
PORecords = null;
}
}
else
{
PORecords = null;
}
RaisePropertyChanged("PONo");
}
}
private List<Tbl_PurchaseOrderMain> _pORecords;
public List<Tbl_PurchaseOrderMain> PORecords
{
get { return _pORecords; }
set
{
_pORecords = value;
RaisePropertyChanged("PORecords");
}
}
private Tbl_PurchaseOrderMain _selectedPO;
public Tbl_PurchaseOrderMain SelectedPO
{
get { return _selectedPO; }
set
{
_selectedPO = value;
if (SelectedPO != null)
{
PONo = SelectedPO.PurchaseOrderRefNo;
}
else
{
PONo = null;
}
RaisePropertyChanged("SelectedPO");
}
}
My problem is that when I enter text on textbox it will populate the listview (PORecords) with three Items.On that moment the selectedPO property is also fired then the textBox is filled the PONo but my listview then have only one Item the other two item gone .Actually I dont select any item on the Listview.I want to populate the textbox only when I select an Item from the listview whats wrong with my code any body have idea?

Converting Grid Content to a ListView or Other for use in MVVM Pattern

I have a 5 X 4 Grid (code below) which works as desired for the shape of my data. I recently discovered it is virtually impossible to pass a grid from the View Model to the View and bind it to another grid in XAML and still maintain the MVVM pattern - which is my goal.
The challenge here is that my presentation requires the children to be grouped in single cells with each having, 1 image, and two textblock UI elements.
DataTable, DataSet, GridView, List etc all seem to lack the ability to add multiple child elements to individual row/column cells for display. Unfortunately this is not just simply sticking an image in a column header.
Has anyone found another option for doing similar?
Thanks,
Glenn
Below is the sample grid and an image of the resulting view.
public void FormatGridCell()
{
Random random = new Random();
List<int> randomNumList = new List<int>();
for (int i = 0; i < 50; i++)
randomNumList.Add(random.Next(50));
List<string> columHeader = new List<string>();
columHeader.Add("Pitts");
columHeader.Add("Vans");
columHeader.Add("Lancair");
columHeader.Add("Epic");
List<string> rowHeader = new List<string>();
rowHeader.Add("NORTH");
rowHeader.Add("SOUTH");
rowHeader.Add("EAST");
rowHeader.Add("WEST");
rowHeader.Add("CANADA");
for (int x = 1; x < 5; x++)
{
var engineType= new TextBlock { Text = columHeader[x - 1] };
engineType.SetValue(Grid.RowProperty, 0);
engineType.SetValue(Grid.ColumnProperty, x);
engineType.HorizontalAlignment = HorizontalAlignment.Center;
this.airplaneGrid.Children.Add(engineType);
for (int r = 1; r < 6; r++)
{
var dealerService = new TextBlock { Text = rowHeader[r - 1] };
dealerService.SetValue(Grid.RowProperty, r);
dealerService.SetValue(Grid.ColumnProperty, 0);
dealerService.HorizontalAlignment = HorizontalAlignment.Center;
this.airplaneGrid.Children.Add(dealerService);
for (int i = 1; i < 6; i++)
{
// Bitmap path will be based on Type
var modelImage = new Image { Width = 20, Height = 20 };
var bitmapImage = new BitmapImage(new Uri(#"c:\personal\temp\dog.jpg"));
modelImage.Source = bitmapImage;
modelImage.SetValue(Grid.RowProperty, r);
modelImage.SetValue(Grid.ColumnProperty, i);
modelImage.HorizontalAlignment = HorizontalAlignment.Left;
modelImage.VerticalAlignment = VerticalAlignment.Top;
var mfgName = new TextBlock { Text = "Lancair IV" };
mfgName.SetValue(Grid.RowProperty, r);
mfgName.SetValue(Grid.ColumnProperty, i);
mfgName.HorizontalAlignment = HorizontalAlignment.Center;
var price = new TextBlock { Text = "$" + randomNumList[r + i] };
price.SetValue(Grid.RowProperty, r);
price.SetValue(Grid.ColumnProperty, i);
price.HorizontalAlignment = HorizontalAlignment.Left;
price.VerticalAlignment = VerticalAlignment.Center;
price.Margin = new Thickness(30, 0, 0, 0);
this.airplaneGrid.Children.Add(modelImage);
this.airplaneGrid.Children.Add(mfgName);
this.airplaneGrid.Children.Add(price);
}
}
}
}
This function is not mine. Sorry, forgot the named credit, but a fellow stackoverflow chap provided to this forum.
public static class RandomExtensions
{
public static int NextDouble(
Random random,
double minValue,
double maxValue)
{
return random.Next(10, 50);
}
}
Sorry, I'm too low on the totem pole to submit an image, but run it for a full understanding of the intended layout.
here is the XAML to support the above.
<Grid x:Name="airplaneGrid" ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition Height="60"/>
<RowDefinition Height="60"/>
<RowDefinition Height="60"/>
<RowDefinition Height="60"/>
<RowDefinition Height="60"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="175"/>
<ColumnDefinition Width="175"/>
<ColumnDefinition Width="175"/>
<ColumnDefinition Width="175"/>
<ColumnDefinition Width="175"/>
</Grid.ColumnDefinitions>
</Grid>
You could use another Grid (or stackpanel, or whatever type of panel) as the child element in each cell and then add the child elements to that panel
Ok. I placed your solution and mine side to side. It looks like this:
Admittedly, it requires a little bit more tweaking, but you get the main idea.
<ListView ItemsSource="{Binding}" Grid.Column="1">
<ListView.Resources>
<DataTemplate x:Key="CellContentTemplate">
<Border BorderBrush="LightGray" BorderThickness="1">
<DockPanel>
<Image Height="20" Width="20" Source="{Binding ImageSource}" Margin="2"
DockPanel.Dock="Left"/>
<StackPanel>
<TextBlock Text="{Binding Value, StringFormat='${0}'}" Margin="2"/>
<TextBlock Text="{Binding Name}" Margin="2"/>
</StackPanel>
</DockPanel>
</Border>
</DataTemplate>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Header="" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="Pitts">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Pitts}" ContentTemplate="{StaticResource CellContentTemplate}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Vans">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Vans}" ContentTemplate="{StaticResource CellContentTemplate}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Lancair">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Lancair}" ContentTemplate="{StaticResource CellContentTemplate}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Epic">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Epic}" ContentTemplate="{StaticResource CellContentTemplate}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Code Behind (just random data):
Random random = new Random();
public GridSample() //Window Constructor
{
InitializeComponent();
var names = new[] {"NORTH","SOUTH","EAST","WEST"};
DataContext = names.Select(x => new GridViewModel()
{
Name = x,
Epic = CreateRandomCell(),
Lancair = CreateRandomCell(),
Pitts = CreateRandomCell(),
Vans = CreateRandomCell()
});
}
private CellViewModel CreateRandomCell()
{
return new CellViewModel
{
Name = "Cell" + random.Next(0, 100),
ImageSource = "/ChessPieces/BlackBishop.png",
Value = (decimal) random.Next(0, 100)
};
}
ViewModels:
public class GridViewModel
{
public string Name { get; set; }
public CellViewModel Pitts { get; set; }
public CellViewModel Vans { get; set; }
public CellViewModel Lancair { get; set; }
public CellViewModel Epic { get; set; }
}
public class CellViewModel
{
public string Name { get; set; }
public string ImageSource { get; set; }
public decimal Value { get; set; }
}
See? pure MVVM, clean, beautiful solution.

Categories