I am new to silverlight and hence new to Bing Map Control as well.
Here's what i am trying to achieve
I've a viewmodel which has 2 properties and looks like this...
public class Vm : INotifyPropertyChanged
{
private LocationCollection _locations;
public LocationCollection Locations
{
get { return _locations; }
set
{
_locations = value;
this.Notify("Locations");
}
}
private Location _selectedLocation;
public Location SelectedLocation
{
get { return _selectedLocation; }
set
{
_selectedLocation = value;
this.Notify("SelectedLocation");
}
}
protected virtual void Notify(string property)
{
if( null != this.PropertyChanged)
{
PropertyChangedEventArgs e = new PropertyChangedEventArgs(property);
this.PropertyChanged(this,e);
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
In my XAML I have a listbox and a Bing Map control. The listbox is bound to locations (Display member is a Latitude for now) and the map control has a MapsItemControl which is bound to Locations as well.
The SelectedItem of ListBox is bound to SelectedLocation and The Center of the map is bound to SelectedLocation as well. So the xaml looks like this
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Path=Locations}"
SelectedItem="{Binding Path=SelectedLocation, Mode=TwoWay}"
DisplayMemberPath="Latitude"
Grid.Row="0"/>
<map:Map
Grid.Row="1"
CredentialsProvider="Av2K1aKwZLPJRS-F_m1TGlFg2bPFVVDgMGbxfFp-1rdpUrwfQmiPSouaSHrHoK-j"
Loaded="Map_Loaded"
Center="{Binding Path=SelectedLocation, Mode=TwoWay}"
ZoomLevel="17">
<map:MapItemsControl x:Name="mapItemsControl" ItemsSource="{Binding Path=Locations}">
<map:MapItemsControl.ItemTemplate>
<DataTemplate>
<map:Pushpin
Location="{Binding}"
/>
</DataTemplate>
</map:MapItemsControl.ItemTemplate>
</map:MapItemsControl>
</map:Map>
</Grid>
As i select the items in the listbox i am able to see that the center of the map changes. However i want to change the Template of Pushpin in this case as well. Let's say that i want to display an image instead of the OOB Pushpin. I understand the concept of how to customize the pushpin however i am unable to get any selection states in MapsItemControl similar to what i can find in other ItemsControl like listbox etc.
Can someone help me on this ??
You could add another MapItemsControl that was bound to only your SelectedLocation and have the other MapItemsControl be bound to all the locations except the SelectedLocation.
That way you could have seperate templates for the selected ones and the unselected ones.
Related
More specifically, am I doing it right because it is not working. I have a bool property in my ViewModel along with a text property for a TextBlock. If I change the text property, the results appear on screen immediately. So I know something is listening for the property changes. The visibility property is set to use a bool-to-visibility converter but that converter never gets called. I'm sure it is just some part of the data binding that I am not doing right but I have tried everything suggested on StackOverflow as well as setting the binding manually and several other things. I have over 12 hours in this problem and am feeling really let down by the whole Silverlight / MVVM architecture in general. And I was so excited that I "figured it out", too.
Particulars: Silverlight 5.1.10144
App.xaml resources:
<Application.Resources>
<vidstreams:ManagementViewModel x:Key="managementViewModel"/>
<vidstreams:VisibilityConverter x:Key="visConverter"/>
</Application.Resources>
MyView.xaml DataContext:
<UserControl.DataContext>
<Binding Source="{StaticResource managementViewModel}"/>
</UserControl.DataContext>
MyView.xaml Grid visibility binding:
<Grid x:Name="LayoutRoot" Background="Black">
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid ...
Visibility="{Binding IsWaitingVisible, Mode=OneWay, Converter={StaticResource visConverter}}">...</Grid>
<Button x:Name="test"
Click="test_Click"
Content="test visibility"
HorizontalAlignment="Left"
Margin="0,0,0,0"
Grid.Row="1"
VerticalAlignment="Top"/>
</Grid>
MyView.xaml.cs Instance property and test_Click code:
public ManagementViewModel DataContextObject
{
get
{
return (ManagementViewModel)App.Current.Resources["managementViewModel"];
}
}
protected void test_Click(object sender, RoutedEventArgs e)
{
DataContextObject.IsWaitingVisibile = !DataContextObject.IsWaitingVisibile; //doesn't toggle the visibility or cause the converter to be hit
DataContextObject.WaitingText = "Loading data..."; //works
}
ManagementViewModel class innards:
public class ManagementViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var p = PropertyChanged;
if (p != null)
{
p(this, new PropertyChangedEventArgs(propertyName));
}
}
bool mIsWaitingVisible = true;
public bool IsWaitingVisibile
{
get { return mIsWaitingVisible; }
set
{
mIsWaitingVisible = value;
OnPropertyChanged("IsWaitingVisible");
}
}
...
}
I would post the converter code here but it isn't even being hit. It's a simple converter like the others found in various posts on this site, anyway.
Any thoughts or suggestions - or just confirmation that this is some sort of regression bug in 5 maybe? - would be so appreciated. Perhaps the visibility binding instructions have to be set differently. Remember, the TextBlock works fine:
<TextBlock x:Name="WaitingTextBlock"
Text="{Binding WaitingText}" .../>
#GolfARama
Hi can you try with this
Visibility="{Binding IsWaitingVisible, Mode=TwoWay, Converter={StaticResource visConverter}}">
I have a UserControl that contains two ContentControls that need to have different UserControl Views bound to them at runtime. The attached-Property solution noted here does not seem to work in Silverlight. Or, I am doing something wrong. I also, found this, but it did not bring any joy either.
I had a single ContentControl working by naming it 'ActiveItem'. But, of course, I cannot have two ContentControls with the same name.
Thanks in advance for any help,
Jim
Just expose two public properties on your main view model, each one being an instance of the type of view model you wish to display. Then, in your view have a ContentControl with the corresponding name. E.g:
public class MyMainViewModel
{
private NavigationViewModel navigation;
private MyContentViewModel main;
public MyMainViewModel()
{
// better to inject factories using constructor injection here
this.Navigation = new NavigationViewModel();
this.Main = new MyContentViewModel();
}
public NavigationViewModel Navigation
{
get { return navigation; }
set { navigation= value; NotifyOfPropertyChanged(() => this.Navigation); }
}
public MyContentViewModel Main
{
get { return main; }
set { main= value; NotifyOfPropertyChanged(() => this.Main); }
}
...
}
<ContentControl x:Name="Navigation" />
...
<ContentControl x:Name="Main" />
This is an old question, but in case anyone is having the same issue, I post here my way of handling it from the beginning and in a more thorough manner:
Your main window that contain both (or even more than two) of your User Controls must be inherited from Caliburn.Micro.Conductor<Screen>.Collection.AllActive;
Your User Controls must be inherited from Caliburn.Micro.Screen;
You must also keep naming conventions in mind. If you use MenuUC as the name of a ContentControl in your View, also create a property named MenuUC in your ViewModel;
Initialize your UserControl as I do in Constructor;
Now you can use ActivateItem(MenuUC) and DeactivateItem(MenuUC) everywhere in your code. Caliburn.Micro automatically detects which one you want to work with.
Example XAML View code:
<Window x:Class="YourProject.Views.YourView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="YourViewTitle" Width="900" Height="480">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="4*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Menu Side Bar -->
<ContentControl Grid.Row="0" Grid.Column="0" x:Name="MenuUC" />
<!-- Panel -->
<Border Grid.Column="1" Grid.RowSpan="2" BorderThickness="1,0,0,0" BorderBrush="#FF707070" >
<ContentControl x:Name="PanelUC" />
</Border>
</Grid>
</Window>
Example C# ViewModel code:
class YourViewModel : Conductor<Screen>.Collection.AllActive
{
// Menu Side Bar
private MenuUCViewModel _menuUC;
public MenuUCViewModel MenuUC
{
get { return _menuUC; }
set { _menuUC = value; NotifyOfPropertyChange(() => MenuUC); }
}
// Panel
private Screen _panelUC;
public Screen PanelUC
{
get { return _panelUC; }
set { _panelUC = value; NotifyOfPropertyChange(() => PanelUC); }
}
// Constructor
public YourViewModel()
{
MenuUC = new MenuUCViewModel();
ActivateItem(MenuUC);
PanelUC = new FirstPanelUCViewModel();
ActivateItem(PanelUC);
}
// Some method that changes PanelUC (previously FirstPanelUCViewModel) to SecondPanelUCViewModel
public void ChangePanels()
{
DeactivateItem(PanelUC);
PanelUC = new SecondPanelUCViewModel();
ActivateItem(PanelUC);
}
}
In the above example, ChangePanels() acts as a method to load new User Control into your ContentControl.
Also read this question, it might be help you further.
I have created a simple MVVM wpf project. The basic Idea is to display data about the annual Income of a customer and the loans he has with various Banks.
The Model consists of 2 Classes , Financial and FinancialLoans.
The ViewModel consists of 2 Classes FinancialVM and FinancialLoanVM
Below are the VM Classes:
namespace WpfTester.ViewModel{
public class FinancialVM
{
public Model.Financial Financial { get; set; }
public ObservableCollection<ViewModel.FinancialLoanVM> FinancialLoanVMs { get; set; }
public FinancialVM()
{
//Fill the models with some sample data
Financial = new WpfTester.Model.Financial { Income = 1950.12 };
Financial.FinancialLoans = new ObservableCollection<Model.FinancialLoan>();
Financial.FinancialLoans.Add(new WpfTester.Model.FinancialLoan { Bank = new Random().Next().ToString() });
Financial.FinancialLoans.Add(new WpfTester.Model.FinancialLoan { Bank = new Random().Next().ToString() });
FinancialLoanVMs = new ObservableCollection<FinancialLoanVM>();
foreach (Model.FinancialLoan financialLoan in Financial.FinancialLoans)
{
FinancialLoanVMs.Add(new ViewModel.FinancialLoanVM { FinancialLoan = financialLoan });
}
} }
public class FinancialLoanVM
{
public Model.FinancialLoan FinancialLoan { get; set; }
public FinancialLoanVM()
{ FinancialLoan = new Model.FinancialLoan(); }
}
}
The UI has a Financial User Ccontrol with it's datacontext bound to the FinancialVM and a FinancialLoan User control with the datacontext Bound to the FinancialLoanVM.
The problem is face, is with the Listbox. I have templated it to have FinancialLoans user controls as Items, but the bound data doesn't get Injected into the FinancialLoanUC DataContext.
I suppose the trick is all in the part of the listboxitem datatemplate.
Any ideas of how i can make this work?
<UserControl.DataContext>
<ViewModel:FinancialVM/>
</UserControl.DataContext>
<Grid d:DataContext="{d:DesignInstance Type=ViewModel:FinancialVM}" >
<Grid.RowDefinitions>
<RowDefinition Height="23"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<TextBlock Text="Income= "/>
<Label Content="{Binding Path=Financial.Income}"/>
</StackPanel>
<ListBox Grid.Row="1" ItemsSource="{Binding Path=FinancialLoanVMs}">
<ListBox.ItemTemplate>
<DataTemplate>
<View:FinancialLoanUC DataContext="{Binding }" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Answering my own question, I found what was wrong.
The FinancialLoanUC had this in the XAML:
<UserControl.DataContext>
<ViewModel:FinancialLoanVM/>
</UserControl.DataContext>
which overrode the DataContext Injected from the FinancialUC. (I suppose what was happening is that DataContext was set from the FinancialLoanVM member of the observable collection and then it was replaced by a new class instance as described in the XAML)
(By asker)
I am writing a WPF application where where i need to display custom file iformation which consists of field name & its value. I generate a grid rumtime with label & textboxes. I display the field name in label & field value in textbox(i want it to be editable). & each time file selection changes, number of field change & so the grid columns & rows. Right now I am generating this grid in code behind . Is there any way i can do it in XAml with view model.
This is pretty easy to do with an ItemsControl. If you ViewModel exposes a list of metadata objects, say a class like this:
public class FileMetaData : INotifyPropertyChanged
{
private string name;
private string value;
public event PropertyChangedEventHandler PropertyChanged = (o, e) => { };
public string Name
{
get { return name; }
set
{
name = value;
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
public string Value
{
get { return value; }
set
{
this.value = value;
PropertyChanged(this, new PropertyChangedEventArgs("Value"));
}
}
}
Then, your ViewModel would expose it as an ObservableCollection (so WPF knows when new items are added or removed):
public class MyViewModel
{
...
public ObservableCollection<FileMetaData> Files { get; private set; }
...
}
Then, your view would use an ItemsControl with an ItemTemplate to display it:
<ItemsControl ItemsSource="{Binding Files}" Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="one" />
<ColumnDefinition Width="Auto" SharedSizeGroup="two" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" />
<TextBox Grid.Column="1" Text="{Binding Value}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Note that I'm setting Grid.IsSharedSizeScope to true on the ItemsControl, so the columns will align. If you have a lot of data, you'll probably want to wrap this in a ScrollViewer (or better retemplate the ItemsControl to have one).
I'm not sure why you're creating this grid at runtime. You should look into using a standard presentation method such as a <ListBox> with a custom item template. Always look to use declaritive definition of your UI (within the XAML) instead of the codebehind.
I've got a blog post on creating a checked listbox that shows some of the details, but you should be able to find other good examples out there as well.
I have a datagrid which contains bid and ask - prices for currency-symbols. The data is updated every seconds. I update the data by creating a new collection of my viewmodel-entities and bind this collection to the datagrid every second.
The problem is:
Because my datagrid contains a template-column with a button "buy", this button is also recreated every second! This means, that when the user hovers the button, the hover-styles blinks, as the button is recreated every second. Additionally sometimes the click-event is not fired correctly, if the button is recreated while the user has his mouse-left-button pressed.
Any suggestions, how to solve real-time-update a datagrid with button-columns?
If I understand correctly, you have a collection of items and you have a couple fields (bid/ask in particular), all of which will be updated every second. It sounds like what may be happening is that in the process of changing the ItemsSource of your data grid, you're losing some important state that is causing problems for the event handlers on your buttons.
Even if you update all of the items, the important distinction to make is that you should update the items, and not completely clear out the collection that is currently bound to your datagrid. Changing the ItemsSource to a new one will cause the data grid to have to do a lot more work than if you simply update the contents of the existing collection. If you're using an ObservableCollection, this may mean making your viewmodel items mutable so that you can simply update bid/ask. If your viewmodel items are mutable and implement INotifyPropertyChanged, the bid/ask updates will be reflected in the datagrid or in any other bindings to those properties of the objects. The neat thing about doing it this way is that the same objects are staying bound to the same containers in the ItemsControl, so during each update, absolutely nothing is happening to your buttons. Now, if your viewmodel objects that contain bid/ask are immutable, you should still be able to pull this off. Every second, you simply iterate through your collection of items and use SetItem to replace each existing item with a new one. The important thing to remember in this latter case is that every second, the datagrid is still getting notified that there has been a change in the ObservableCollection, and because of this, the bindings on each row are going to cause the DataContext of the row/cells/button to update.
Here's a quick example of how I might go about this problem. I'm going to assume use of the datagrid in .NET 4.0 (if you're using toolkit though with 3.5, this should be the same). I'm going to take the first approach, where my CurrencyPair object is mutable.
First, some simple viewmodel code with a self contained timer to update a few currency pair bid/asks each second:
public class CurrencyPairsViewModel
{
private readonly Dispatcher _dispatcher = Dispatcher.CurrentDispatcher;
private readonly ObservableCollection<string> _orders = new ObservableCollection<string>();
private readonly ObservableCollection<CurrencyPair> _pairs = new ObservableCollection<CurrencyPair>();
private readonly Random _rand = new Random();
private readonly System.Timers.Timer _timer = new System.Timers.Timer(1000);
private readonly Action _update;
public CurrencyPairsViewModel()
{
this._timer.Elapsed += OnIntervalElapsed;
this._update = new Action(this.Update);
this._pairs.Add(new CurrencyPair("USD/GBP"));
this._pairs.Add(new CurrencyPair("AUD/USD"));
this._pairs.Add(new CurrencyPair("WOW/CAD"));
this._timer.Start();
}
public ObservableCollection<string> Orders { get { return this._orders; } }
public ObservableCollection<CurrencyPair> Pairs { get { return this._pairs; } }
public void Buy(CurrencyPair pair)
{
this._orders.Add(string.Format("Buy {0} at {1}", pair.Name, pair.Ask));
}
private void OnIntervalElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
this._dispatcher.Invoke(this._update);
}
private void Update()
{
foreach (var pair in this._pairs)
{
pair.Bid = this._rand.NextDouble();
pair.Ask = pair.Bid + 0.01;
}
this._timer.Start();
}
}
public class CurrencyPair : INotifyPropertyChanged
{
private readonly string _name;
private double _ask;
private double _bid;
public CurrencyPair(string name)
{
this._name = name;
}
public event PropertyChangedEventHandler PropertyChanged;
public double Ask
{
get { return this._ask; }
set
{
this._ask = value;
this.OnPropertyChanged("Ask");
}
}
public double Bid
{
get { return this._bid; }
set
{
this._bid = value;
this.OnPropertyChanged("Bid");
}
}
public string Name { get { return this._name; } }
protected void OnPropertyChanged(string name)
{
if (null != this.PropertyChanged)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
Second, the view, which in this example is just my MainWindow.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="4"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DataGrid Grid.Row="0"
ItemsSource="{Binding Pairs}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="*"/>
<DataGridTextColumn Header="Bid" Binding="{Binding Bid}" Width="*"/>
<DataGridTextColumn Header="Ask" Binding="{Binding Ask}" Width="*"/>
<DataGridTemplateColumn Header="Buy">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="BUY"
Click="OnBuyClicked"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<GridSplitter Grid.Row="1" Height="4" HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
<ListBox Grid.Row="2"
ItemsSource="{Binding Orders}"/>
</Grid>
</Window>
And finally, I have a bit of code behind this XAML to handle the BUY button clicks and initialize a viewmodel right in the view (note that this, and other practices outside of how to update the bid/ask on the collection of items may not be the best way to go about things, depending on how your application is going to grow).
public partial class MainWindow : Window
{
private readonly CurrencyPairsViewModel _model = new CurrencyPairsViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = this._model;
}
private void OnBuyClicked(object sender, RoutedEventArgs e)
{
var pair = (CurrencyPair)((Button)sender).DataContext;
this._model.Buy(pair);
}
}
Hope the example is helpful!
Have you looked into the ObservableCollection?
Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
This should only refresh those items that are changed, rather than the whole grid.