I'm filtering a listcollectionview in viewModel 1 based on selectionchanged of datagrid in viewModel 2. To do this I use mvvm messaging. Each time the selection of the datagrid changes a message is send to update my listcollectionview. This all works well.
Now I need to use the string value of this message to pass into the filter. The problem is that I can only use the string value in the updateShotList method but not into the bool IsMatch. How can I make this work, or how can I use the string value of the message as a variable in my viewmodel.
This is how my viewmodel looks like.
private ObservableCollection<Shot> _allShots = new ObservableCollection<Shot>();
public ObservableCollection<Shot> AllShots
{
get { return _allShots; }
set { _allShots = value; RaisePropertyChanged();}
}
private ListCollectionView _allShotsCollection;
public ListCollectionView AllShotsCollection
{
get
{
if (_allShotsCollection == null)
{
_allShotsCollection = new ListCollectionView(this.AllShots);
}
return _allShotsCollection;
}
set
{
_allShotsCollection = value; RaisePropertyChanged();
}
}
private void UpdateShotList(string SceneNr) // value sceneNr comes from message from viewmodel 2
{
_allShotsCollection.IsLiveFiltering = true;
_allShotsCollection.Filter = new Predicate<object>(IsMatchFound);
}
bool IsMatchFound(object obj)
{
=====> if (obj as Shot != null && (obj as Shot).SceneNumber == "?????") // Here I need the value of string ScenNr that comes from the message.
{
return true;
}
return false;
}
public ShotListViewModel()
{
Messenger.Default.Register<string>(this, "UpdateShotList", UpdateShotList);
}
You can create your predicate as a lambda expression and close over SceneNr to capture it:
_allShotsCollection.Filter = o =>
{
var shot = o as Shot;
return shot != null && shot.SceneNumber == SceneNr;
};
Alternatively, simply introduce an instance variable to contain your filter string and update it each time you receive a message:
private string _sceneNr;
private void UpdateShotList(string sceneNr)
{
// ...
_sceneNr = sceneNr;
}
bool IsMatchFound(object obj)
{
var shot = obj as Shot;
return shot != null && shot.SceneNumber == _sceneNr;
}
Related
I want to add a tooltip to a menu item. On the menu there is the word "DELETE" and when the mouse hovers over the word I want a tooltip displayed. I though of using 'ToolTipService.SetToolTip();'.
This is where the items contained in the menu are set...
protected virtual void SetContextMenuItems()
{
// -- Add condition for ReadOnly + ReadOnly Attribute to AreaEntity
if (this.ViewMode == Common.Core.ViewModes.RealTime)
{
AreaEntity ae = viewModel.EntityViewContext as AreaEntity;
if (((UserContext.Instance.IsAdmin() && (ae.Scope.Value == "global" || ae.Scope.Value == string.Empty)) ||
ae.OwnerPosition.Value == CoreServices.Instance.CurrentPosition.Configuration.Name)
&& !((this.MapInstance.Parent as Grid).Parent is PIPMap))
{
menuItem = new ContextMenuItem();
//menuItem.DisplayText = "Delete"; // -- Could be dynamic based off type "Edit Polygon (Circle, etc.)"
menuItem.DisplayText = CoreServices.Instance.GetString("Delete");
cmd = new MR.CommandBridge.Common.Command.DelegateCommand(DeleteShape, CanDelete);
menuItem.Command = cmd;
this.ContextMenu.MenuItems.Add(menuItem);
}
}
}
Methods 'DeleteShape' and 'CanDelete':
public void DeleteShape(object param)
{
EntityStore.Instance.DeleteEntity(this.ViewModel.EntityViewContext);
}
public bool CanDelete(object param)
{
GetRulesForShape();
bool isInFilter = false;
EntityCollection<Entity> lists = EntitySync.Instance.Cache["entityCollection"];
foreach (Entity list in lists)
{
isInFilter = (list as ListEntity).FilterList.Filters.Count(a => (a.FilterType == FilterTypes.WithinZone && a.Value == this.viewModel.EntityViewContext.Uri) ||
(a.FilterType == FilterTypes.MultipleFilter && a.Filters.Count(b => b.FilterType == FilterTypes.WithinZone && b.Value == this.viewModel.EntityViewContext.Uri) > 0)) > 0;
if (isInFilter) break;
}
return !HasRules && !CoreServices.Instance.ZoneFilters.Contains(this.viewModel.Area.Uri) && gfEditor.dm != GeofenceEditor.DrawMode.DrawEdit && !isInFilter;
}
Ok I made some adjustments to your class.
Somehow I got the feeling your mixing up things like control and bindings.
We'll see. ;)
I've also made some comments, maybe you can shed some light over then.
public class ContextMenuItem : MenuItem
{
public ContextMenuItem()
:base()
{
}
//Replace by Header
//
//public string DisplayText { get; set; }
//Can this be replaced by build in CommandParameter
//
private Dictionary<string, object> _parameters = new Dictionary<string, object>();
private Func<ContextMenuItem, List<ContextMenuItem>> _getMenuItems = null;
//Already available
//public DelegateCommand Command { get; set; }
//What does this function do?
public Func<ContextMenuItem, List<ContextMenuItem>> GetMenuItems
{
get
{
return _getMenuItems;
}
set
{
_getMenuItems = value;
}
}
public Dictionary<string, object> Parameters
{
get
{
return _parameters;
}
}
//Can be replaced by base Items
//
//private List<ContextMenuItem> _menuItems = new List<ContextMenuItem>();
//public List<ContextMenuItem> ChildMenuItems
//{
// get
// {
// return _menuItems;
// }
//}
private bool _isChecked = false;
public bool IsChecked
{
get { return _isChecked; }
set { _isChecked = value; }
}
// -- Command or implementer could provide a handler for all commands - might be simpler for now
// -- I think there could be a better way to route commands but I'll thin on it.
Could this simply be done in .css?
.yourclass:hover{
cursor:pointer;
}
or target it with jquery?
Have you tried this?
menuitem.ToolTip = "Delete";
Normally a contextmenu can exist of regular MenuItems. I used it often.
;)
Context menu items have the ToolTipText property:
menuItem.ToolTipText = "ToolTip Text Here";
I have two drop down lists. Niether of them have a relation ship with each other. But I need to filter one drop down list based on the chosen value of another drop down list.
I can filter it in code. When I debug I can see the filtered results on the property. However when I run the app, it does not work. Here is my code so far:
private BindingList<Commodity> _AllocationCommodities;
[Browsable(false)]
public BindingList<Commodity> AllocationCommodities
{
get
{
if (_AllocationCommodities == null)
{
_AllocationCommodities = new BindingList<Commodity>();
ChangeCommodities();
}
return _AllocationCommodities;
}
}
private SourceEntity _SourceEntity;
[ImmediatePostData]
[Association("SourceEntity-LimitAllocations")]
[RuleRequiredField("RuleRequiredField_LimitAllocation_SourceEntity", DefaultContexts.Save)]
public SourceEntity SourceEntity
{
get
{
return _SourceEntity;
}
set
{
//New Code
if (SetPropertyValue<SourceEntity>("SourceEntity", value))
{
if (IsSaving || IsLoading) return;
ChangeCommodities();
}
}
}
private Commodity _Commodity;// This is the drop down to be filtered
[ImmediatePostData]
[DataSourceProperty("AllocationCommodities")] //// This Attribute Should filter Commodities
[RuleRequiredField("RuleRequiredField_LimitAllocation_Commodity", DefaultContexts.Save)]
public Commodity Commodity
{
get
{
return _Commodity;
}
set
{
SetPropertyValue("Commodity", ref _Commodity, value);
if (Commodity.Oid != Guid.Empty)
AllocationVolumeUnits.Reload();
}
}
private void ChangeCommodities()
{
if (!this.IsLoading && _SourceEntity != null)
{
_AllocationCommodities.RaiseListChangedEvents = false;
_AllocationCommodities.Clear();
OperandValue[] _params;
System.Collections.Generic.List<CMSBOD.SourceCommodity> _sc = new System.Collections.Generic.List<SourceCommodity>();
BindingList<Commodity> _Commodities = new BindingList<Commodity>();
foreach (SourceCommodityEntity _tempSCE in _SourceEntity.SourceCommodityEntities)
{
if (_tempSCE.SourceCommodity != null)
_sc.Add(_tempSCE.SourceCommodity);
}
foreach (SourceCommodity _tempSC in _sc)
{
if (_tempSC.Commodity != null && !_Commodities.Contains<Commodity>(_tempSC.Commodity) && _tempSC.Commodity.IsActive)
_Commodities.Add(_tempSC.Commodity);
}
_AllocationCommodities.RaiseListChangedEvents = true;
_AllocationCommodities = _Commodities;///This is where I can see the filtered list when debugging.
}
}
You can find a DataSourceCriteria useful in this scenario, instead of DataSourceProperty.
Assuming you have collection properties that associates Commodity back to SourceCommodityEntity, you can use this criteria:
[DataSourceCriteria("IsActive And SourceCommodities[SourceCommodityEntities[SourceEntity = '#SourceEntity'] ]")]
Even if its designed to be a 1x1 assocation, you can find that associations can be useful for filtering purposes.
Here's my property that determines if I should bind the other properties or not:
public string LotNumber {
get {
return lotNumber;
}
set {
using (var db = new DDataContext()) {
lotNumber = value;
// Check for duplicates
bool isDuplicate =
db.LotInformation.Any(r => r.lot_number == lotNumber);
if (isDuplicate == true) {
ComponentsList = null;
FamiliesList = null;
ExpirationDate = null;
LotNumber = null;
lotNumber = null;
// Inform user that the lot_number already exists
errorWindow.Message =
LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(
LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(
LanguageResources.Resource.Lot_Exists_Already);
return;
} else {
lotNumber = value;
}
RaisePropertyChanged("LotNumber");
}
}
}
My problem right now is if I upload a file and if the lot number already exists in the database, the boolean returns true and an error message is thrown. However, after that,it loops again and then the boolean is set to false since now the value is null and it still binds the data afterward. How can I break out of the loop and just make it stop running/clear/prevent binding when bool is true in the case above?
I assume you have some code like this:
LotNumber = "ABC5"; // ABC5 already exists in the database - uh oh!
And then you're trying to figure everything out in the "setter". It's already too late by that point. Instead, move your logic into separate methods:
private bool LotNumberExists(string lotNumber)
{
using (var db = new DDataContext())
{
return db.LotInformation.Any(r => r.lot_number == lotNumber);
}
}
private void ClearFields()
{
ComponentsList = null;
FamiliesList = null;
ExpirationDate = null;
LotNumber = null;
}
private void InformUserOfDuplicate()
{
// Inform user that the lot_number already exists
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(LanguageResources.Resource.Lot_Exists_Already);
}
Then check the return value of that method before setting LotNumber.
private void SomeOtherMethod()
{
string someLotNumber = "ABC5";
if (LotNumberExists(someLotNumber)
{
ClearFields();
InformUserOfDuplicate();
return;
}
LotNumber = someLotNumber;
}
Turn your setter back into a simple setter without a ton of logic wrapped up in it:
public string LotNumber
{
get { return lotNumber; }
set
{
lotNumber = value;
RaisePropertyChanged("LotNumber");
}
}
I've the following:
public enum ValidationSeverity
{
Error = 1,
Warning = 2
}
Public class Errors
{
public ValidationSeverity Severity { set; get; }
public string Desc { set; get; }
}
in the ViewModel defined ObservableCollection, and I bound it to DataGrid of the sdk, now I've two toggle buttons:
Show Errors
Show Warnings
-
When I click on "Show Errors", the data grid will just have the rows that the Severity of them is "Error".
I'm trying to use ICollectionView, like that when I click on "Show Errors", it will go to:
private void OnShowErrors()
{
if (IsErrorButtonChecked)
Show(ValidationSeverity.Error);
else
Hide(ValidationSeverity.Error);
}
private void Hide(ValidationSeverity sev)
{
var lcv = _collectionViewSourceHelper.GetCollectionView(ErrorsList);
if (lcv == null || !lcv.CanFilter) return;
lcv.Filter = item =>
{
var error = item as Error;
if (error == null) return false;
return error.Severity != sev;
};
}
private void Show(ValidationSeverity sev)
{
var lcv = _collectionViewSourceHelper.GetCollectionView(ErrorsList);
if (lcv == null || !lcv.CanFilter) return;
lcv.Filter = item =>
{
var error = item as Error;
if (error == null) return false;
return error.Severity == sev;
};
}
_collectionViewSourceHelper - I added this, cause in the Silverlight we can't use the GetCollectionView directly, now my question is how I can do that, I wrote two predicates, but how I can continues, if I edit the collectionView does it cause changing the view?
Thanks
The view is just a wrapper around the collection, if you edit the collection the changes will be reflected in the collectionview also, so basically you must edit the ErrorList.
I'm having trouble getting my UI to update Two Listboxes' to update properly when my ViewModel changes.
First, the basic logic behind the page:
Movie is an object with a title, and a variety of MovieDetails. Some MovieDetail are different than others, as they are detailed which is a glorified way of saying they're more Important.
I use two ListBoxes to separate these MovieDetails into stacked ListBoxes, one for 'Detailed' and one for 'NotDetailed'
If a movie has no 'Detailed' attributes, the corresponding list is Hidden via a BooleanToVisibilityConverter (and vice-versa)
When I navigate to the page, I set the Movie the page corresponds to, and it should RaisePropertyChanged to alert the AllMoviesDetail ObservableCollection that it should re-get Movies.MovieDetailFetchedList.
From there, AllMoviesDetail would alert the two ObservableCollections (Detailed, NotDetailed) they should be re-get.
In fact, RaisePropertyChanged on NotDetailedMovieDetails or DetailedMovieDetails does not seem to do anything either. (And the corresponding HasNotDetailedMovieDetails, Has...)
What does work, however, is if I add more items into the list, the CollectionChanged event seems to fire and reactivate the list. I have also been able to do this by instantiating the ObservableCollections in code first var temp = DetailedMoviesDetail;
public class MoviesDetailViewModel : ViewModelBase
{
#region Property Names
public const string MoviePropertyString = "Movie";
public const string AllMoviesDetailPropertyString = "AllMoviesDetail";
public const string DetailedMoviesDetailPropertyString = "DetailedMoviesDetail";
public const string NotDetailedMoviesDetailPropertyString = "NotDetailedMoviesDetail";
public const string HasNotDetailedMoviesDetailPropertyString = "HasNotDetailedMoviesDetail";
public const string HasDetailedMoviesDetailPropertyString = "HasDetailedMoviesDetail";
public const string NotDetailedHeaderPropertyString = "NotDetailedHeader";
#endregion
public MoviesDetailViewModel()
{
if (IsInDesignMode)
{
Movie = DesignDataStore.MovieList[0];
Movie.Category = Category.DDA;
}
}
private Movie _Movie;
/// <summary>
/// The Movie for which to browse MoviesDetail. It is expected when setting this property, that MoviesDetail for it have been downloaded previously.
/// </summary>
/// <remarks>The 'Master' property for this ViewModel. All properties are Dependent on this and the underlying property MoviesDetailList</remarks>
/// <seealso cref="MovieDetailFetchedList"/>
public Movie Movie
{
get { return _Movie; }
set
{
if (_Movie != value)
{
if (_Movie != null)
_Movie.MovieDetailFetchedList.CollectionChanged -= MoviesDetailListChanged;
_Movie = value;
RaisePropertyChanged(MoviePropertyString);
RaisePropertyChanged(StatementPeriodAvailablePropertyString);
RaisePropertyChanged(NotDetailedMoviesDetailPropertyString);
Movie.MovieDetailFetchedList.CollectionChanged += MoviesDetailListChanged;
RaisePropertyChanged(AllMoviesDetailPropertyString);
RaisePropertyChanged(DetailedMoviesDetailPropertyString);
RaisePropertyChanged(NotDetailedHeaderPropertyString);
}
}
}
private void MoviesDetailListChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (var item in e.NewItems)
{
if (((MovieDetail) item).IsDetailed())
DetailedMoviesDetail.Add(item as MovieDetail);
else
NotDetailedMoviesDetail.Add(item as MovieDetail);
}
}
else
{
RaisePropertyChanged(AllMoviesDetailPropertyString);
RaisePropertyChanged(DetailedMoviesDetailPropertyString);
RaisePropertyChanged(NotDetailedMoviesDetailPropertyString);
}
}
#endregion
private MovieDetailFetchedList _allMoviesDetail;
public MovieDetailFetchedList AllMoviesDetail
{
get
{
if (Movie == null)
return new MovieDetailFetchedList();
return _allMoviesDetail ?? (AllMoviesDetail = Movie.MovieDetailFetchedList);
}
set
{
if (_allMoviesDetail != value)
{
if (_allMoviesDetail != null)
_allMoviesDetail.CollectionChanged -= MoviesDetailListChanged;
_allMoviesDetail = value;
_allMoviesDetail.CollectionChanged += MoviesDetailListChanged;
RaisePropertyChanged(AllMoviesDetailPropertyString);
//force update
DetailedMoviesDetail = NotDetailedMoviesDetail = null;
RaisePropertyChanged(DetailedMoviesDetailPropertyString);
RaisePropertyChanged(HasDetailedMoviesDetailPropertyString);
RaisePropertyChanged(NotDetailedMoviesDetailPropertyString);
RaisePropertyChanged(HasNotDetailedMoviesDetailPropertyString);
}
}
}
public bool HasNotDetailedMoviesDetail { get { return NotDetailedMoviesDetail != null && NotDetailedMoviesDetail.Count > 0; } }
private ObservableCollection<MovieDetail> _notDetailedMoviesDetail;
public ObservableCollection<MovieDetail> NotDetailedMoviesDetail
{
get
{
if (Movie == null) return new ObservableCollection<MovieDetail>();
return AllMoviesDetail;
return _notDetailedMoviesDetail ?? //make sure RaisePropertyChanged happens by using property setter
(NotDetailedMoviesDetail = AllMoviesDetail.Where(mem => !mem.IsDetailed()).ToObservableCollection());
}
set
{
_notDetailedMoviesDetail = value;
RaisePropertyChanged(NotDetailedMoviesDetailPropertyString);
RaisePropertyChanged(HasNotDetailedMoviesDetailPropertyString);
}
}
public bool HasDetailedMoviesDetail
{ get { return DetailedMoviesDetail != null && DetailedMoviesDetail.Count > 0; } }
private ObservableCollection<MovieDetail> _DetailedMoviesDetail;
public ObservableCollection<MovieDetail> DetailedMoviesDetail
{
get
{
if (Movie == null) return new ObservableCollection<MovieDetail>();
return AllMoviesDetail;
return _DetailedMoviesDetail ?? //make sure RaisePropertyChanged happens by using property setter
(DetailedMoviesDetail = AllMoviesDetail.Where(mem => mem.IsDetailed()).ToObservableCollection());
}
set
{
_DetailedMoviesDetail = value;
RaisePropertyChanged(DetailedMoviesDetailPropertyString);
RaisePropertyChanged(HasDetailedMoviesDetailPropertyString);
}
}
private string _DetailedHeader;
public string DetailedHeader
{
get { return _DetailedHeader ?? (_DetailedHeader = AppResources.in_available); }
set { _DetailedHeader = value; }
}
public string NotDetailedHeader
{
get { return (Movie != null && Movie.Category == Category.DRAMA) ? AppResources.Movie_MoviesDetail : AppResources.not_in_available; }
}
}
All of your property getters (except AllMoviesDetail) have two return statements. Since only the first will be executed, the values are not being assigned and the PropertyChanged events are not being twiggered.