I have added a boolean property to show/hide the busy indicator, but in my case it is not showing the busy indicator when the relay command method starts execution.
XAML CODE:
`
<Button Grid.Row="6" Grid.Column="1"
Content="LoadDBNames"
Height="28" Width="100"
Style="{StaticResource SubmitButton}"
Command="{Binding LoadDB, Mode=OneWay}"
IsEnabled="{Binding IsRunButtonEnabled}"
Margin="0 2 0 2"></Button>
<xamltk:BusyIndicator x:Name="busyFirstTab"
Grid.RowSpan="6"
Grid.ColumnSpan="2"
BusyContent="Please wait..."
IsBusy="{Binding IsLoaderVisible,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
`
View model code
Public class ViewMOdel:ViewMOdelBase
{
public ObservableCollection<ItemControl> DBNames
{
get => _DBNames;
set
{
_DBNames = value;
OnPropertyChanged(nameof(DBNames));
}
}
//In the constructor of viewmodel
LoadDB = new RelayCommand(LoadDbNames, Canexecutemethod);
//Method
LoadDbNames(object parameter)
{
IsLoaderVisible = true;
try
{
//DB Interaction
DBName.Add(item);
}
catch(){
//log exception
}
finally()
{
IsLoaderVisible = false;
}
}
I tried using background worker and dispatcher for showing the indicator.
While using background worker I got the below exception
"This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.****"
as I am using the Observable collection forrealtime update in UI.
ObservableCollection is created on UI thread, we can only modify it from UI thread and not from other threads.
Related
i have a comboxbox that while it is beign populated i want it replaced in the UI by a message saying it is being loaded.
i did this by using a textbox showing the message and giving both objects visibility bindings in the view model (IsShowAuthComboBox &LoadingAuthenticationMsg)
here's the XAML code
<ComboBox x:Name="ComboBoxAuthSource"
Grid.Row="3"
Style="{StaticResource ComboBoxStyle}"
SelectedItem ="{Binding SelectedAuthenticationSource,UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding AuthenticationSource,UpdateSourceTrigger=PropertyChanged}"
Visibility= "{Binding IsShowAuthComboBox, Converter={StaticResource BoolToVis}}" />
<TextBox x:Name="ComboBoxAuthCover"
Grid.Row="3" Grid.Column="{StaticResource TableColumn}"
Style="{StaticResource FieldBoxStyle }"
FontSize="12"
IsReadOnly="True"
Visibility="{Binding IsShowGettingAuthenticationMsg, Converter={StaticResource BoolToVis}}"
Text="{Binding LoadingAuthenticationMsg,UpdateSourceTrigger=PropertyChanged,Mode=OneWay,FallbackValue='Loading authentication sources...'}" />
And here's the viewModel
public bool IsShowAuthComboBox
{
set
{
if (_isShowAuthenticationComboBox != value)
{
_isShowAuthenticationComboBox = value;
OnPropertyChanged("IsShowAuthComboBox");
OnPropertyChanged("IsShowGettingAuthenticationMsg");
}
}
get =>_isShowAuthenticationComboBox;
}
public bool IsShowGettingAuthenticationMsg => !_isShowAuthenticationComboBox;
public virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
Log.Write(LogClass.General, LogLevel.Debug,
$"{propertyName} update triggerd",
_moduleName);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
this code is the first thing that happens in the relevant flow, but i will sometimes only see it at the very end of the execution and for only for an instant.
at other times it will work as expected.
what am i missing here?
EDIT :
this also accurs when validating the IP ,simpler code.
here's the code
public string SelectedServer
{
get => _selectedServer;
set
{
lock (_lockObj)
{
IsShowAuthComboBox = false;
if (!IsValideIp(value))
//some code
IsShowAuthComboBox = true;
}
}
bool IsValideIp(string ip)
{
//some code
//calls the server sync
return RemotingConfigurator.GetServerConfig(ip).isValid;
}
Your issue is that you are setting the IsShowAuthComboBox property and calling the IsValideIp synchronously on the same thread. And a single thread cannot both update the UI and query a database simultaneously.
What you should do is to call the IsValideIp on a background thread. I wouldn't do this in the setter of a property though, but rather in a command. You may want to read #Stephen Cleary's blog post on the subject.
this is what i ended up doing. moved the UI changes away from the data layer and into the viewModel (SetUiOnWait)
public string SelectedServer
{
get => _selectedServer;
set
{
//IsShowAuthComboBox = false;
SetUiOnWait(true);
Log.Write(LogClass.General, LogLevel.Debug,
$"Server changed from {_selectedServer} to {value} by user",
_moduleName);
_selectedServer = value;
OnPropertyChanged();
// OnPropertyChanged();
//workaround for when changing servers when a unique
//authentication source is selected causes the selected source to be null :\
if (AuthenticationSource.Any())
{
SelectedAuthenticationSource = AuthenticationSource[0];
}
Task.Factory.StartNew(() =>
{
LoginInfo.SelectedServer = _selectedServer;
}).ContinueWith((t) =>
{
if(t.Exception !=null)
{
ExceptionLog.Write(t.Exception.GetBaseException(),_moduleName);
}
RefreshAuthenticationProperties();
OnPropertyChanged("IsLimitedClinicalUse");
OnPropertyChanged("IsNotForClinicalUse");
SetUiOnWait(false);
});
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
dispatcher.Invoke((Action)(() =>
{
//PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}));
}
Task.Factory.StartNew() forces and logic to be executed on a new thread and for the UI changes to wait for it be completed.
and invoke within OnPropertyChange forces the event to be handled by the UI thread.
I have a Listview in my application for which i need to bind more than 1000 items. while fetching the data i am able to display a message to the user saying "fetching items....". once fetching the data completes "fetching items.." message got disappeared and after some time my data is being displayed in listview.
basically message is disappearing during databinding time. so my question is, how can i display "fetching items..." message until my listview completes binding of all the items ?
can anyone please suggest me some ideas on how to implement this.
thanks in advance.
You can achieve this by putting your ListView and the TextBlock in a Grid. And switch between them using the visibility. Use a Task to fetch your Items and then show your ListView when the Task is finished.
Xaml:
<Grid>
<TextBlock Text="yourText" Visibility="{Binding TextBlockVisibility}" HorizontalAlignment="Center"/>
<ListView ItemsSource="{Binding yourItems}" Visibility="{Binding ListViewVisibility}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel></StackPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>
and your ViewModel:
private Visibility _textBlockVisibility;
public Visibility TextBlockVisibility
{
get { return _textBlockVisibility; }
set
{
_textBlockVisibility = value;
RaisePropertyChanged();
}
}
private Visibility _listViewVisibility;
public Visibility ListViewVisibility
{
get { return _listViewVisibility; }
set
{
_listViewVisibility = value;
RaisePropertyChanged();
}
}
private async void StartFetchItems()
{
ShowTextBlock();
await Task.Factory.StartNew(FetchItemsAsync).ContinueWith(task2=>ShowListView());
}
private async Task FetchItemsAsync()
{
....fetchyourItems
}
private void ShowTextBlock()
{
TextBlockVisibility=Visibility.Visible;
ListViewVisibility=Visibility.Collapsed;
}
private void ShowListView()
{
ListViewVisibility=Visibility.Visible;
TextBlockVisibility=Visibility.Collapsed;
}
Thanks for your time reading my thread.
I am using VS2012, WFP, and .net4.5 on Windows 7 64bit
I have a ListView control with xaml in following:
<ListView Name="lvViewerControl"
SelectionMode="Single"
SelectedIndex="{Binding Path=ViewType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Background="{x:Null}"
BorderBrush="{x:Null}"
Margin="2">
<Label Name="lblView2D"
Width="40"
Height="40"
Margin="3"
Background="#FF595959"
Foreground="White"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center">
<Image Source="/CaptureSetupModule;component/Icons/2D.png" />
</Label>
<Label Name="lblView3D"
Width="40"
Height="40"
Margin="3"
Background="#FF595959"
Foreground="White"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center">
<Image Source="/CaptureSetupModule;component/Icons/3D.png" />
</Label>
<Label Name="lblViewTiles"
Width="40"
Height="40"
Margin="3"
Background="#FF595959"
Foreground="White"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Visibility="{Binding Path=XYCtrlVisible, Mode=TwoWay, Converter={StaticResource boolToVis}}">
<Image Source="/CaptureSetupModule;component/Icons/Tile.png" />
</Label>
</ListView>
Now I want to collapse the third item, lblViewTiles. I tried to combine its Visibility to a bool, then do the bool to visibility conversion, but it did not work. What I mean by not work is that the Visiblity only collapses when the application starts (the application loads the xml to get the Visibility as it starts). Afterwards, not matter how the binding variable (Visiblity) changes, and the value does change to Collapsed, but the lblViewTiles still in the ListView control, no UI change.
Here is how DataContext are binded:
The DataContex of the ListView is binded to CaptureSetupModules class. The ListView is defined in LiveVM class. The action which is the loading of the xml is in MasterView class. So in order to access the Visibility property in CaptureSetupModules, I simply created a CaptureSetupModules object in MasterView class,
In MasterView class
CaptureSetupModules _captureVM = new CaptureSetupModules();
...
LiveVM _liveVM = new LiveVM;
if (ndList.Count > 0)
{
xyBorder.Visibility = ndList[0].Attributes["Visibility"].Value.Equals("Visible") ? Visibility.Visible : Visibility.Collapsed;
tilesControlBorder.Visibility = ndList[0].Attributes["Visibility"].Value.Equals("Visible") ? Visibility.Visible : Visibility.Collapsed;
this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
_captureVM.XYCtrlVisible = ndList[0].Attributes["Visibility"].Value.Equals("Visible") ? true:false;
}
)
);
}
And here is my converter:
public sealed class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var flag = false;
if (value is bool)
{
flag = (bool)value;
}
else if (value is bool?)
{
var nullable = (bool?)value;
flag = nullable.GetValueOrDefault();
}
if (parameter != null)
{
if (bool.Parse((string)parameter))
{
flag = !flag;
}
}
if (flag)
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
}
This code only makes the item collapsed the first time when the application starts , and it loads the visibility from the xml file. Afterwards, not matter how the XYCtrlVisible, the visibility binding, changes, the UI show no response. The item is always there, or not there.
Basically the problem is: the binded variable changes, the xml file changes as well, but the UI does not change, exception for the first time when the application launches as it loads the xml.
It is probably a little messy here, let me know if you need anything else. I am pretty confused myself too. Thanks.
On the property XYCtrlVisible you need to implement INotifyPropertyChanged
That is how the UI is notified of a changed
Why are you binding that two way?
I currently have an application with a user interface in Windows Forms. The code behind this user interface communicates with a service.
For example, I have the following code:
public partial class MainWindow : Window
{
private KeyLessAccessLogic ServiceLogic;
public MainWindow()
{
InitializeComponent();
ServiceLogic = new KeyLessAccessLogic();
//LoadValues();
}
public KeyLessAccessLogic MyServiceLogic
{
get { return ServiceLogic; }
set
{
ServiceLogic = value;
// RaisePropertyChanged("MyServiceLogic");
}
}
private void BindDataSource()
{
cmb_user_name.DataSource = null;
cmb_user_name.Sorted = false;
cmb_user_name.DataSource = ServiceLogic.Users;
cmb_user_name.DisplayMember = "name";
}
And my XAML:
<ComboBox Height="23" HorizontalAlignment="Left" Margin="6,71,0,0"
Name="cmb_user_update" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding Path=MyServiceLogic.Users}" DisplayMemberPath="name" />
Now I recreated the UI in WPF, and I'm a bit lost on the new format. I do believe that example I gave here is one of the examples of the difference between WPF and Windows Forms.
How can I let my application know what the datasource should be of the Dropdown-box cmb_user_name? ServiceLogic is the central block of my service, accessing for example the database.
As a second thing, I have a listbox to show me some devices. I tried to approach the datasource differently to show what else I have tried:
<ListBox Height="100" HorizontalAlignment="Left" Margin="6,44,0,0"
Name="listBox_detected" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding Path=ServiceLogic.TheDevicesList}" DisplayMemberPath="name" />
Use XAML for that:
<ComboBox ItemsSource="{Binding MyServiceLogic.Users}"
SelectedItem="{Binding User}"
DisplayMemberPath="name" />
Create a property ServiceLogic in your ViewModel to hold a ServiceLogic object:
private ServiceLogic myServiceLogic;
public ServiceLogic MyServiceLogic
{
get { return myServiceLogic; }
set
{
myServiceLogic = value;
RaisePropertyChanged("MyServiceLogic");
}
}
I assume Users is ObservableCollection. Or you can create a property which holds Users collection directly.
Background, from MSDN:
ObservableCollections CollectionChanged event will only be raised
when properties of ObservableCollection are changed (Addition,
deletion of an element) and not when the properties of existing elements are changed.
Bummer, because I need the UI to update when a specific property of an existing element changes. I tried firing both CollectionChanged events and PropertyChanged Events but neither worked.
My situation:
In my application, I have a listbox bound to an observablecollection where the visibility of the items depends on the "Favorite" property of each item using a BoolToVisibilityConverter. XAML:
<ListBox x:Name="FavoritesListBox"
Margin="0,0,-12,0"
ItemsSource="{Binding FeedItemOCollection}"
SelectionChanged="FavoritesListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Name="FavoritesStackPanel"
Margin="0,0,0,17" Visibility="{Binding Favorite, Converter={StaticResource BooltoVisibilityConverter}}">
<TextBlock Text="{Binding Title}"
TextWrapping="Wrap"
Margin="12,0,0,0"
Style="{StaticResource PhoneTextLargeStyle}" />
<TextBlock Text="{Binding PublishDate,Converter={StaticResource DateTimeToDateConverter}}"
TextWrapping="Wrap"
Margin="12,-6,12,0"
Style="{StaticResource PhoneTextSmallStyle}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note: This object is initialized in App.Xaml.cs and so is global for the whole application. This may be the unusual thing that is causing things not to work for me.
Once the initial binding occurs, changes to the value of an element's Favorite property does not cause the item to show up or disappear from the Favorites Listbox as is desired for the reason noted at the beginning of the post. This is expected.
To work around this I've tried firing both CollectionChanged events and PropertyChanged Events when the Favorite property is changed to get the UI to update, but neither worked and I'm confused why not. I have succeed in working around my issue, by adding and removing the element from the ObservableCollection, but clearly this is a hack. Code:
public void MarkFavorite(FeedItem feedItem)
{
try
{
feedItem.Favorite = true;
//CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); <-- why doesn't this work?
//PropertyChanged(this, new PropertyChangedEventArgs("Count")); <-- why doesn't this work?
this.Remove(feedItem); <-- this works, but is a hack
this.Add(feedItem); <-- this works, but is a hack
SaveToIso();
}
catch (Exception exception)
{
//TODO: Log this.
}
}
Why doesn't firing the events work?
Many thanks ahead of time.
Your FeedItem class must implement INotifyPropertyChanged interface, and your Favorite property must look like:
private bool _Favorite;
private bool _Favorite;
public bool Favorite
{
get { return _Favorite; }
set
{
_Favorite = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Favorite"));
}
}
Or you can extract a method
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
and your property will look like this:
private bool _Favorite;
public bool Favorite
{
get { return _Favorite; }
set
{
_Favorite = value;
OnPropertyChanged("Favorite");
}
}