I have two windows one to add users and the other to view all the users, both of them are connected the database. What I want is to show the user automatically in the view all windows after I press the add button in the add users window (before closing it).
In view all window (I am posing the data binding parts only):-
Note:
The following code is in the constructor of MainWindows.xaml.
Basically the ProfileControl is a custom control, profileList is
ListBox and profileCollection is an ObservableCollection
if (dbObj.GetDbData().Count != 0)
{
foreach (ProfileControl item in dbObj.GetDbData())
{
profileCollection.Add(item);
}
this.profileList.DataContext = profileCollection;
}
The following is for the ListBox.
<ListBox Name="profileList" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True">
<ListBox.ItemTemplate>
<DataTemplate>
<profileControl:ProfileControl Name="pcList" onClickUser="ProfileControl_onClickUser" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
And lastly the following is the add button which is in a AddUser.xaml.
Note: Basically I am checking first that the user I am adding is check-in or not. If not I am adding it to the database.
private void addButton_Click(object sender, RoutedEventArgs e)
{
try
{
if (co.checkIfCheckedIn(memoryCard))
{
MessageBox.Show("The person is inside. ", "No Adding", MessageBoxButton.OK, MessageBoxImage.Stop);
}
else
{
co.AddData(memoryCard, emailTb.Text, contactNumberTb.Text, workPlaceTb.Text, infoTb.Text);
MessageBox.Show("Saving is done. ", "Saved", MessageBoxButton.OK, MessageBoxImage.Asterisk);
this.notificationTB.Text = " Saving is complete.";
}
}
catch (Exception ex)
{
this.notificationTB.Text = ex.Message;
this.notificationTB.Foreground = (Brush)new BrushConverter().ConvertFromString("White");
this.notificationTB.Background = (Brush)new BrushConverter().ConvertFromString("Red");
}
}
I want to add that, it should after I save the data by the add button shows the user in the MainWindows automatically, but it not. And I have to close the application and open it again to view the data. I believe I need to access the ObservableCollection object profileCollection in the MainWindows.xaml to do this but I am not sure if it is right and how.
Please advise me, thank.
There is at least 3 solutions from the simplest (bad from a dogmatic "decouple them all" POV but can be pragmatic) to the mass plumbing (good from a design POV):
1) share your collections via a global resource in Application.Current.Resources:
Application.Current.Resources["MyUserCollection"] = theUserCollection;
2) use a ViewModel shared by both views, exposing the users collection; you'll typically set it as the DataContext of both views
3) use a message broker to inform the main view that a new user was added, you'll need some MVVM framework for that like MVVM Light or Caliburn Micro
EDIT: here is another solution which is a good compromise:
4) in your MainWindow code-behind expose a Refresh/Reload method:
public void ReloadUsers()
{
profileCollection.Clear();
var data = dbObj.GetDbData();
if (data.Count != 0)
{
foreach (ProfileControl item in data)
{
profileCollection.Add(item);
}
this.profileList.DataContext = profileCollection;
}
}
And call it from your other Window:
private void addButton_Click(object sender, RoutedEventArgs e)
{
try
{
...
(Application.Current.MainWindow as MainWindow).ReloadUsers();
}
catch (Exception ex)
{
...
}
}
Related
This question already has answers here:
WPF ListView: Changing ItemsSource does not change ListView
(7 answers)
Closed 6 years ago.
I have a little problem and I don't understand where it comes from, I suppose when I will get the answer I will probably said the famous " aaaaaahhhhh yessss ! of course !" so here his my problem : I try to update a listView in mvvm with a drag and drop, with the breakpoints and stuff I can see that the List<string> that goes into the listView is updated and has a new item inside, the element that I pass to the listView Items, but the view doesn't update and the new Item doesn't appear. Here is my code :
private List<string> _listViewItems;
public List<string> listViewItems
{
get
{
return _listViewItems;
}
set
{
_listViewItems = value;
OnPropertyChanged("listViewItems");
}
}
public ICommand MouseMove { get; set; }
//in constructor
MouseMove = new BaseCommand(GoMouseMove);
private void GoMouseMove(object obj)
{
MouseEventArgs e = (MouseEventArgs)obj;
try
{
if (e.LeftButton == MouseButtonState.Pressed)
{
draggedItem = (TreeViewItem) SelectedItem;
if (draggedItem != null)
{
DragDropEffects finalDropEffect = DragDrop.DoDragDrop(SelectedItem, SelectedItem, DragDropEffects.Move);
//Checking target is not null and item is dragging(moving)
if ((finalDropEffect == DragDropEffects.Move))
{
CopyItem(draggedItem, _target);
_target = null;
draggedItem = null;
}
}
}
}
catch (Exception)
{
}
}
private void CopyItem(TreeViewItem _sourceItem, ListViewItem _targetItem)
{
//Asking user wether he want to drop the dragged TreeViewItem here or not
if (MessageBox.Show("Would you like to drop " + _sourceItem.Header.ToString(), "", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
try
{
List<string> items = listViewItems;
items.Add(_sourceItem.Header.ToString());
listViewItems = items;
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
}
When i debug i got that :
<ListView Name="listview1"
Grid.ColumnSpan="2"
Width="auto"
Height="auto"
Grid.Row="1"
AllowDrop="True"
ItemsSource="{Binding listViewItems, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Drop" >
<cmd:EventToCommand Command="{Binding Drop}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
So we can see that algo.pdf is added, but the view doesn't update. What am I missing ?!
Thank you very much !
A List<> has no way to inform WPF that an item has been added, and therefore WPF can not update the UI to display any added items.
Try using an ObservableCollection<> which will send notificiation events to WPF when you add / remove items.
Also, remember that the items inside your collection should also implement INotifyPropertyChanged if you want WPF to update when properties inside those objects change.
I am trying to display to alert box after tapping or click on the selected ListView box. Currently the selectedItem is binding to SelectedTicket, which trigger an object call SelectedTicket within the ViewModel. Once the SelectedTicket is triggered, it then executes a popup.show() method with the DisplayAlert() method. The DisplayAlert() method get executes but does not display the AlertBox.
==============================================
////Xaml Page
<ListView ItemsSource="{Binding TicketList,Mode=OneWay}" RowHeight="130"
ItemTemplate="{StaticResource TicketListTileTemplate}" BackgroundColor="#d9deeb"
SelectedItem="{Binding SelectedTicket, Mode=TwoWay}" SeparatorVisibility="None">
</ListView>
==================================================
////ViewModal
public object SelectedTicket
{
get
{
return _SelectedTicket;
}
set
{
if (SetProperty(ref _SelectedTicket, value))
{
if ((value != null) && (value is TicketListItem))
{
popup1.Show();
SelectedTicket = null;
}
}
}
}
======================================================
////Popup.cs
async public void Show()
{
DisplayAlert("Alert", "Your have been alerted", "OK");
}
One of the most common reasons I don't see a DisplayAlert is because it is being called on a Page that isn't active on screen.
As a quick workaround/test you can do
await Application.Current.MainPage.DisplayAlert("Alert", "Your have been alerted", "OK");
If this works, my first assumption is confirmed.
I always try to keep my code behind clean, hence calling from the ViewModel is certainly a good approach. Normally your MVVM Library has some code to help with display alerts.
DisplayAlert() is only available to Page objects (such as ContentPage or NavigationPage) see here, your Popup.cs might not be a Page object. Also you are not awaiting DisplayAlert which you always want to do with async methods. Finally, your Show() method may not be running on the UI thread.
Instead of trying to show the alert from your ViewModel, why don't you try displaying the alert from the code-behind of your XAML page like this:
XAML:
<ListView ItemsSource="{Binding TicketList,Mode=OneWay}"
RowHeight="130"
ItemTemplate="{StaticResource TicketListTileTemplate}" BackgroundColor="#d9deeb"
SelectedItem="{Binding SelectedTicket, Mode=TwoWay}"
SeparatorVisibility="None"
ItemSelected="OnItemTapped"> <!-- Notice ItemTapped here will trigger when an item is tapped, imagine that -->
In Code-behind:
....
private TicketViewModel _viewModel = new TicketViewModel();
....
public async void OnItemTapped (object o, ItemTappedEventArgs e) { //Notice I added 'async' to the method here, that is so we can 'await' the DisplayAlert below (ALWAYS 'await' the DisplayAlert and ANY other async methods)
TicketListItem item = (TicketListItem)o;
if (item != null) {
await DisplayAlert("Alert", "Your have been alerted", "OK"); //Notice the 'await' here
_viewModel.SelectedTicket = null;
}
}
I'm learning Universal Windows Platform and I'm currently analysing music player using SoundCloud API from this site. Link to the github project is at the very bottom of the page. To get this project to work variable SoundCloudClientId from App.xaml.cs should be filled in (I used client id from previous example on the same page, not sure if I can paste that).
When application starts the NowPlaying page is loaded and changing tracks causes UI to update accordingly. The problem is when I navigate to any other page and return back to NowPlaying. I can still change music using buttons, but UI doesn't change (song title, album title etc.).
Important parts of the code:
NowPlaying.xaml
<ImageBrush x:Name="albumrtImage" ImageSource="Assets\Albumart.png" Stretch="UniformToFill" />
<TextBlock x:Name="txtSongTitle" Grid.Row="0" HorizontalAlignment="Center" Text="Song Title " FontSize="25" Foreground="White" Style="{StaticResource HeaderTextBlockStyle}" TextTrimming="WordEllipsis" />
<TextBlock x:Name="txtAlbumTitle" Grid.Row="0" Text="Label " HorizontalAlignment="Center" FontWeight="Light" FontSize="20" Foreground="#9799a5" Style="{StaticResource BodyTextBlockStyle}" TextTrimming="WordEllipsis"/>
NowPlaying.xaml.cs
async void BackgroundMediaPlayer_MessageReceivedFromBackground(object sender, MediaPlayerDataReceivedEventArgs e)
{
TrackChangedMessage trackChangedMessage;
if (MessageService.TryParseMessage(e.Data, out trackChangedMessage))
{
// When foreground app is active change track based on background message
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
var songIndex = GetSongIndexById(trackChangedMessage.TrackId);
if (songIndex >= 0)
{
var song = App.likes[songIndex];
LoadTrack(song); //Update UI
}
});
return;
}
BackgroundAudioTaskStartedMessage backgroundAudioTaskStartedMessage;
if (MessageService.TryParseMessage(e.Data, out backgroundAudioTaskStartedMessage))
{
backgroundAudioTaskStarted.Set();
return;
}
}
private async void LoadTrack(SoundCloudTrack currentTrack)
{
try
{
//Change album art
string albumartImage = Convert.ToString(currentTrack.artwork_url);
if (string.IsNullOrWhiteSpace(albumartImage))
{
albumartImage = #"ms-appx:///Assets/Albumart.png";
}
else
{
albumartImage = albumartImage.Replace("-large", "-t500x500");
}
//Next 3 lines when pages were switched don't cause UI to update
albumrtImage.ImageSource = new BitmapImage(new Uri(albumartImage));
txtSongTitle.Text = currentTrack.title;
txtAlbumTitle.Text = Convert.ToString(currentTrack.user.username);
}
catch (Exception ex)
{
MessageDialog showMessgae = new MessageDialog("Something went wrong. Please try again. Error Details : " + ex.Message);
await showMessgae.ShowAsync();
}
}
After navigating from NowPlaying->Me->NowPlaying and clicking next the track changes, but UI doesn't update as seen on the screen below:
UI problem
I'm trying to reproduce the problem on a simple example, but without any luck. What could cause this issue? Any help is appreciated.
I've found the solution. The problem was with cache. The NowPlaying page required putting following property:
NavigationCacheMode="Required"
I just want to add that you can also check navigation mode in OnNavigatedTo method. It can be helpful when for instance you would like to refresh the data when returning to the selected page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if(e.NavigationMode == NavigationMode.Back)
{
//refresh the data here...
}
}
Basically what I am trying to do is have it where you can type something into a ComboBox and it autocompletes to something from it's drop down, but the user shouldn't be able to enter their own entry.
I know that by default if you have "isEditable" equal to true then it autocompletes on it's own. However you can still enter in whatever you want. I want to prevent this.
This is how I am pulling my names in
void populateNames()
{
nameBox = this.nameTextBox;
APICaller Caller = new APICaller();
try
{
List<string> listOfNames = Caller.APIGetNames();
foreach (string a in listOfNames)
{
Console.WriteLine(a);
nameBox.Items.Add(a);
}
}
catch (Exception e)
{
Console.WriteLine("Something went wrong: " + e);
}
nameBox.SelectedIndex = 0;
}
and the ComboBox in the XAML
<ComboBox
Name="nameTextBox" Height="23" Width="Auto" Margin="10,0,10,97" VerticalAlignment="Bottom" IsEditable="True"
PreviewTextInput="tbxPreviewTextInput" DataObject.Pasting="tbxPasting" LostFocus="nameTbxLostFocus"
GotFocus="nameTbxGotFocus" PreviewKeyDown="classTextBox_PreviewKeyDown" HorizontalAlignment="Stretch"/>
Under your properties tab, set your AutoComplete source to the list of items that populates the combo box. Then, set your AutoCompleteMode to Suggest or SuggestAppend.
I'm creating a windows Phonw 8.1 app. I have a ListBox and I want to append/insert other at runtime when I click a button. But it doesn't work or it crashes.
I my page's constructor I have this:
myData = new List<Stuff>() {
new Stuff(){Name="AAA"},
new Stuff(){Name="BBB"},
new Stuff(){Name="CCC"},
new Stuff(){Name="DDD"},
};
myListBox.DataContext = myData;
My page's xaml:
<ListBox x:Name="myListBox" ItemsSource="{Binding}" Background="Transparent">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontSize="20" Foreground="Red"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Ok, this work well, when I launch the app I can see the list with its 4 items.
private void Button_Tapped(object sender, TappedRoutedEventArgs e)
{
myData.Add(new Stuff() { Name = String.Format("Added item #{0}", myData.Count) });
//I tried to set the DataContext again, but it does nothing
myListBox.DataContext = mydata;
//I tried to tell the the list to redraw itself, in winform, the Invalidate() method usually get the job done, so I tried both
myListBox.InvalidateMeasure()
//and / or
myListBox.InvalidateArrange();
//or
myListBox.UpdateLayout();
//I tried
myListBox.Items.Add("Some text");
//or
myListBox.Items.Add(new TextBlock(){Text="Some text"});
//or
(myListBox.ItemsSource as List<Stuff>).Add(new Stuff(){Name="Please work..."});
}
And the best the can can happen is to throw an Exception:
An exception of type 'System.Exception' occurred in mscorlib.ni.dll but was not handled in user code
Additional information: Catastrophic failure (Exception from HRESULT: 0x8000FFFF (E_UNEXPECTED))
I've also used a ListView instead, but nothing changes. Bonus question: what is the difference between a ListBox and a ListView?
Google is not really helping, the stuff I find is probably for old version of Windows Phone or normal WPF or ASP.Net...
Also, a weird thing that happens is after adding an item to the list and nothing happen, when I click on an old item, I get a catastrophic failure. I don't have event on my list items yet.
I'm about to give up with data binding, and just build my app piece by piece by code. It should not be that hard to add stuff to a list, what am I doing wrong?
Need to implement something with INotifyPropertyChanged Interface either do it yourself or you use a class that has it built in.
MSDN INotifyPropertyChanged Interface
Try
using System.ComponentModel;
using System.Collections.ObjectModel;
public class Stuff
{
public Stuff(string name)
{
this.Name = name;
}
public string Name { get; set; }
}
ObservableCollection<Stuff> myData = new ObservableCollection<Stuff>();
myData.Add(new Stuff("abcd"));