I am working on a WPF project and I am facing a problem. I have searched for 2 days a solution but I have never found anything that could help me...
I have a TreeView with different data types inside and a ContentControl that shows views corresponding to these different data types. I want, when I click on a TreeViewItem that, depending on the data type contained in this TreeViewItem, it changes the view in the ContentControl.
I reached to execute different commands depending on the TreeviewItem selected but it never changes the view...
Does somebody have an answer or an idea that could help me ?
I use this for my TreeView :
<TreeView x:Name="networkTree" ScrollViewer.VerticalScrollBarVisibility="Auto" MaxHeight="490" TreeViewItem.Selected="GetHeaderSelectedItem"/>
It executes this :
public class NetworkConfigViewModel : BindableBase
{
private IRegionManager regionManager;
private bool _showRDConf;
private bool _showNetworkConf;
private bool _showDeviceInfo;
public NetworkConfigViewModel(IRegionManager regionManager)
{
this.regionManager = regionManager;
regionManager.RegisterViewWithRegion("NetworkConfigInfoRegion", typeof(NetworkConfigInfoView));
regionManager.RegisterViewWithRegion("RDConfigurationRegion", typeof(RDConfigurationView));
regionManager.RegisterViewWithRegion("DeviceInfoRegion", typeof(DeviceInfoView));
ShowNetworkConfCommand = new DelegateCommand(NetworkConf);
ShowRDConfCommand = new DelegateCommand(RDConf);
ShowDeviceInfoCommand = new DelegateCommand(DevInfo);
_showNetworkConf = true;
_showRDConf = false;
_showDeviceInfo = false;
}
public bool ShowRDConf
{
get
{
return _showRDConf;
}
set
{
SetProperty(ref _showRDConf, value);
}
}
public bool ShowNetworkConf
{
get
{
return _showNetworkConf;
}
set
{
SetProperty(ref _showNetworkConf, value);
}
}
public bool ShowDeviceInfo
{
get
{
return _showDeviceInfo;
}
set
{
SetProperty(ref _showDeviceInfo, value);
}
}
public DelegateCommand ShowNetworkConfCommand { get; set; }
public DelegateCommand ShowRDConfCommand { get; set; }
public DelegateCommand ShowDeviceInfoCommand { get; set; }
private void NetworkConf()
{
ShowRDConf = false;
ShowDeviceInfo = false;
ShowNetworkConf = true;
System.Windows.Forms.MessageBox.Show("Commande ShowNetConf executée :\nShowNetworkConf="+ShowNetworkConf.ToString()+"\nShowRDConf="+ShowRDConf.ToString() + "\nShowDeviceInfo=" + ShowDeviceInfo.ToString());
}
private void RDConf()
{
ShowNetworkConf = false;
ShowDeviceInfo = false;
ShowRDConf = true;
System.Windows.Forms.MessageBox.Show("Commande ShowRConf executée :\nShowRDConf="+ShowRDConf.ToString()+"\nShowNetworkConf="+ShowNetworkConf.ToString() + "\nShowRDeviceInfo=" + ShowDeviceInfo.ToString());
}
private void DevInfo()
{
ShowNetworkConf = false;
ShowRDConf = false;
ShowDeviceInfo = true;
System.Windows.Forms.MessageBox.Show("Commande ShowDeviceInfo executée :\nShowDeviceInfo=" + ShowDeviceInfo.ToString() + "\nShowNetworkConf=" + ShowNetworkConf.ToString() + "\nShowRDConf=" + ShowRDConf.ToString());
}
public void ChangeNetworkView(TreeView tree)
{
TreeViewItem tvi = null;
tvi = tree.SelectedItem as TreeViewItem;
if (tvi != null)
{
if (tvi.Tag.ToString() == "Network")
{
ShowNetworkConfCommand.Execute();
}
else if(tvi.Tag.ToString()=="RD")
{
ShowRDConfCommand.Execute();
}
else if (tvi.Tag.ToString() == "VD")
{
ShowDeviceInfoCommand.Execute();
}
}
}
}
And for my ContentControl :
<ContentControl x:Name="RDView" x:FieldModifier="public" Grid.Column="0" Grid.Row="1" Grid.RowSpan="2" Grid.ColumnSpan="2" Visibility="{Binding Path=ShowRDConf, Converter={StaticResource Convert}}" prism:RegionManager.RegionName="RDConfigurationRegion"/>
<ContentControl x:Name="NetworkView" x:FieldModifier="public" Grid.Column="0" Grid.Row="1" Grid.RowSpan="2" Grid.ColumnSpan="2" Visibility="{Binding Path=ShowNetworkConf, Converter={StaticResource Convert}}" prism:RegionManager.RegionName="NetworkConfigInfoRegion"/>
<ContentControl x:Name="DeviceView" x:FieldModifier="public" Grid.Column="0" Grid.Row="1" Grid.RowSpan="2" Grid.ColumnSpan="2" Visibility="{Binding Path=ShowDeviceInfo, Converter={StaticResource Convert}}" prism:RegionManager.RegionName="DeviceInfoRegionq"/>
Thank you by advance
Related
I am trying to bind DataGrid using MVVM approach in WPF, model is getting values but nothing is showing in DataGrid
Following is my code
public class TalleyEditorGrid : INotifyPropertyChanged
{
#region Properties
private string _Quantity;
private string _Ft;
private string _Inch;
private string _Comment;
public string Quantity { get { return _Quantity; } set { _Quantity = value; NotifyPropertyChanged("Quantity"); } }
public string Ft { get { return _Ft; } set { _Ft = value; NotifyPropertyChanged("Ft"); } }
public string Inch { get { return _Inch; } set { _Inch = value; NotifyPropertyChanged("Inch"); } }
public string Comment { get { return _Comment; } set { _Comment = value; NotifyPropertyChanged("Comment"); } }
#endregion
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); }
}
In my xaml.cs
private ObservableCollection<TalleyEditorGrid> _TalleyEditorGrid = new ObservableCollection<TalleyEditorGrid>();
public ObservableCollection<TalleyEditorGrid> TalleyEditorCol
{
get { return _TalleyEditorGrid; }
}
On button click I am filling this collection
_TalleyEditorGrid.Add(new TalleyEditorGrid() { Quantity = Q, Ft = FT, Inch = In, Comment = Comment});
Xaml as follows
<DataGrid x:Name="TalleyEditor" ItemsSource="{Binding Path=TalleyEditorCol}" AutoGenerateColumns="True" Visibility="Collapsed"
CanUserAddRows="False"
HorizontalGridLinesBrush="{x:Null}"
VerticalGridLinesBrush="Silver"
Background="White"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
SelectionMode="Single"
SelectionUnit="FullRow"
CanUserReorderColumns="True"
CanUserSortColumns="True" DataGridCell.GotFocus="TalleyEditor_GotFocus"
RowHeaderWidth="0" HorizontalAlignment="Left" VerticalAlignment="Top" RowHeight="17" ColumnHeaderHeight="21" PreviewKeyDown="TalleyEditor_PreviewKeyDown" LostFocus="TalleyEditor_LostFocus" CellEditEnding="myDG_CellEditEnding">
Set the DataContext of the window to itself:
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
Or set the DataContext of the DataGrid:
public MainWindow()
{
InitializeComponent();
TalleyEditor.DataContext = this;
}
If you put the DataGrid inside another DataGrid, you need to use a {RelativeSource} to be able to bind to a property of the parent window:
<DataGrid ... ItemsSource="{Binding Path=TalleyEditorCol, RelativeSource={RelativeSource AncestorType=Window}}">
</DataGrid>
I have a simple ListBox and a TextBox as under.I want to display the selectedvalue property of Listbox in the textbox,but my ViewModel's selected object is always null.
What am i missing here?
My XAML
<StackPanel>
<Canvas>
<TextBox x:Name="TxtMail" Width="244" FontSize="14" Canvas.Left="36" Canvas.Top="34" Height="20" Text="{Binding CurrentRec.Name,Mode=OneWay}" />
<ListBox x:Name="AllMatching" Width="{Binding ElementName=TxtMail,Path=Width}" Height="100" Canvas.Top="54" Canvas.Left="36" DisplayMemberPath="Name" SelectedItem="{Binding CurrentRec,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" SelectedValue="Name" SelectedValuePath="Name" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto" />
<Button Content="Test" x:Name="cmdtest" Click="cmdtest_Click"/>
</Canvas>
My ViewModel:
public class VM_Data : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int p_ID;
public double p_SP, p_CP;
public string p_Name;
public List<DM_Data> AllData;
public DM_Data CurrentRec;
public VM_Data()
{
LoadData();
}
public int ID
{
get { return p_ID; }
set
{
if (p_ID != value)
{
RaisePropertyChangedEvent("ID");
p_ID = value;
}
}
}
public double SP
{
get { return p_SP; }
set
{
if (p_SP != value)
{
RaisePropertyChangedEvent("SP");
p_SP = value;
}
}
}
public double CP
{
get { return p_CP; }
set
{
if (p_CP != value)
{
RaisePropertyChangedEvent("CP");
p_CP = value;
}
}
}
public string Name
{
get { return p_Name; }
set
{
if (p_Name != value)
{
RaisePropertyChangedEvent("Name");
p_Name = value;
}
}
}
private void LoadData()
{
AllData = new List<DM_Data>();
string[] strNames = "Jatinder;Shashvat;shashikala;shamsher;shahid;justin;jatin;jolly;ajay;ahan;vijay;suresh;namita;nisha;negar;zenith;zan;zen;zutshi;harish;hercules;harman;ramesh;shashank;mandeep;aman;amandeep;amarjit;asim;akshay;amol;ritesh;ritivik;riz;samana;samaira;bhagwandass;bhagwan;bhawna;bhavna".Split(';');
for(int i=0;i<=strNames.GetUpperBound(0);i++)
{
DM_Data NewRec = new DM_Data();
NewRec.CP = new Random().Next(200, 400);
NewRec.SP = new Random().Next(1, 10);
NewRec.ID = i + 1;
NewRec.Name = strNames[i];
AllData.Add(NewRec);
}
AllData = AllData.OrderBy(item => item.Name).ToList();
}
private void RaisePropertyChangedEvent(string Property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(Property));
}
}
}
My DataModel
public class DM_Data
{
public int p_ID;
public double p_SP, p_CP;
public string p_Name;
public int ID
{
get { return p_ID; }
set { p_ID = value; }
}
public double SP
{
get { return p_SP; }
set { p_SP = value; }
}
public double CP
{
get { return p_CP; }
set { p_CP = value; }
}
public string Name
{
get { return p_Name; }
set { p_Name = value; }
}
MainWindow.Xaml.cs
public partial class MainWindow : Window
{
VM_Data ViewModel;
public MainWindow()
{
InitializeComponent();
ViewModel = new VM_Data();
this.DataContext = ViewModel;
AllMatching.ItemsSource = ViewModel.AllData;
}
private void cmdtest_Click(object sender, RoutedEventArgs e)
{
DM_Data crec = ViewModel.CurrentRec;
}
}
CurrentRec must be a property that raises the PropertyChanged event:
private DM_Data _currentRec;
public DM_Data CurrentRec
{
get { return _currentRec; }
set { _currentRec = value; RaisePropertyChangedEvent("CurrentRec"); }
}
In the code you have posted, it is a field and you cannot bind to fields:
public DM_Data CurrentRec;
You can't bind to fields! CurrentRec must be a property. At now it is a field.
Why do you set ItemsSource in code-behind? Set it in XAML.
You should call RaisePropertyChangedEvent after you've changed backing field, not before.
It is not right pattern for events raising: if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(Property)); } You need to save event delegate to variable first or use ?.Invoke.
Don't create new instances of Random on every iteration of loop because you will get equal values. Create the only one outside of the loop and use it.
What you are doing is kind of MVVM, but not really.
Here is a quick fix anyway:
Please take a look at the Bindings.
<StackPanel>
<Canvas>
<TextBox x:Name="TxtMail" Width="244" FontSize="14" Canvas.Left="36" Canvas.Top="34" Height="20" Text="{Binding ElementName=AllMatching, Path=SelectedItem.Name}" />
<ListBox x:Name="AllMatching"
Width="{Binding ElementName=TxtMail,Path=Width}"
Height="100"
Canvas.Top="54"
Canvas.Left="36"
DisplayMemberPath="Name"
SelectedItem="{Binding CurrentRec,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto" />
<Button Content="Test" x:Name="cmdtest" Click="cmdtest_Click"/>
</Canvas>
</StackPanel>
I think you get the idea at: Text="{Binding ElementName=AllMatching, Path=SelectedItem.Name}".
Aditional Information
First:
You fire to early dude. Please first assign the value and then say its changed.
if (p_Name != value)
{
RaisePropertyChangedEvent("Name");
p_Name = value;
}
Second:
Use a ObservableCollection<DM_Data> to let your ListBox know about changes.
Third:
Use the posibility of Binding
Remove AllMatching.ItemsSource = ViewModel.AllData; and go like
<ListBox x:Name="AllMatching"
ItemsSource="{Binding Path=AllData}"
...
/>
And after all of this - please man check out some tutorials. And also refactor your code from VM_Data to DataViewModel thank you sir.
Here is code of my ViewModel:
public BindableCollection<ICaller> Callers { get { return callService.Current.Callers; } }
public void TalkPrivate(ICaller caller)
{
callService.TalkPrivate(caller);
}
public bool CanTalkPrivate(ICaller caller)
{
return caller.OnHold || callService.IsConference;
}
The XAML:
<ItemsControl ItemsSource="{Binding Callers}" Grid.Row="0" Margin="10,22,10,10"
Visibility="{Binding Callers.Count, Converter={StaticResource CollectionSizeToVisibilityConverter}, ConverterParameter=false}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="120"/>
</Grid.ColumnDefinitions>
<controls:CircleContentControl Height="40" Width="40" Grid.Column="0">
<Image Source="{Binding Image}" Stretch="Uniform" />
</controls:CircleContentControl>
<TextBlock Text="{Binding Display}" Grid.Column="1" FontSize="24" VerticalAlignment="Center" Margin="5,0,0,0"/>
<StackPanel Grid.Column="2" Orientation="Horizontal">
<Button cal:Message.Attach="TalkPrivate($dataContext)" Style="{StaticResource CallActionButtonStyle}" ToolTip="Falar privado"
Height="50" Width="50">
<Rectangle Width="20" Height="20" Fill="{Binding Path=Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}">
<Rectangle.OpacityMask>
<VisualBrush Stretch="Fill" Visual="{DynamicResource appbar_phone}" />
</Rectangle.OpacityMask>
</Rectangle>
</Button>
Call Service will change OnHold property of the Callers. However, the UI is not changed accordinly; that is, the buttons are not disabled/enabled. It seems CanTalkPrivate is not called after TalkPrivate method is called.
How can I force the button availability to refresh?
EDITED for more insights of my code:
CallerViewModel
public class CallerViewModel : PropertyChangedBase, ICaller
{
public CallerViewModel(string phoneNumber, string name, string image = null)
{
PhoneNumber = phoneNumber;
Name = name;
Display = name;
Image = image;
}
public CallerViewModel(string phoneNumber, Contact contact)
: this(phoneNumber, contact.Display, contact.Image) { }
private string name;
public string Name
{
get { return name; }
set
{
name = value;
NotifyOfPropertyChange();
}
}
private string display;
public string Display
{
get { return display; }
set
{
display = value;
NotifyOfPropertyChange();
}
}
private string phoneNumber;
public string PhoneNumber
{
get { return phoneNumber; }
set
{
phoneNumber = value;
NotifyOfPropertyChange();
}
}
private string image;
public string Image
{
get { return image; }
set
{
image = value;
NotifyOfPropertyChange();
}
}
private bool onHold;
public bool OnHold
{
get { return onHold; }
set
{
onHold = value;
NotifyOfPropertyChange();
}
}
public void AppendToDisplay(string value)
{
if (Display == Name)
Display = value;
else
Display += value;
}
public void ResetDisplay()
{
Display = Name;
}
}
}
CallService:
public class CallService : ICallService
{
private readonly ICallerSearch callerSearch;
public bool IsInCall { get { return Current != null; } }
public bool IsConference { get; private set; }
public ICurrentCall Current { get; private set; }
public string IncomingPhoneNumber { get; set; }
public CallService(ICallerSearch callerSearch)
{
this.callerSearch = callerSearch;
}
public ICurrentCall CreateCall(string number)
{
var caller = callerSearch.FindByNumber(number);
Current = new CurrentCall(caller);
return Current;
}
public ICurrentCall CreateCall(ICaller caller)
{
Current = new CurrentCall(caller);
return Current;
}
public void EndCall(ICaller caller = null)
{
if (caller == null)
EndAll();
else
{
Current.Callers.Remove(caller);
if (IsConference)
IsConference = false;
}
}
private void EndAll()
{
if (Current != null)
Current.Dispose();
Current = null;
}
public ICaller AddCaller(string number)
{
foreach (var caller in Current.Callers)
caller.OnHold = true;
var newCaller = callerSearch.FindByNumber(number);
Current.Add(newCaller);
return newCaller;
}
public void MergeCalls()
{
IsConference = true;
}
public void TalkPrivate(ICaller caller)
{
foreach (var item in Current.Callers)
item.OnHold = true;
caller.OnHold = false;
if (IsConference)
IsConference = false;
}
}
#nigel, ICaller is in fact a ViewModel already. But ICallService isn't.
EDIT2 - SOLUTION
#Nigel pointed in the right direction. What I did was: CallerViewModel listen to ICallService ConferenceStarted and ConferenceEnded and update the CanTalkPrivate:
public CallerViewModel(ICallService callService, string phoneNumber, string name, string image = null)
{
this.callService = callService;
callService.ConferenceStarted += ConferenceStarted;
PhoneNumber = phoneNumber;
Name = name;
Display = name;
Image = image;
}
private void ConferenceStarted(object sender, System.EventArgs e)
{
NotifyOfPropertyChange(() => CanTalkPrivate);
}
private bool onHold;
public bool OnHold
{
get { return onHold; }
set
{
onHold = value;
NotifyOfPropertyChange();
NotifyOfPropertyChange(()=>CanTalkPrivate);
}
}
public bool CanTalkPrivate
{
get
{
return OnHold || callService.IsConference;
}
}
However, I can't use the guard method, because it still doesn't work. But now I can bind IsEnabled to CanTalkPrivate:
<Button cal:Message.Attach="TalkPrivate($dataContext)" IsEnabled="{Binding CanTalkPrivate}" Style="{StaticResource CallActionButtonStyle}" ToolTip="Falar privado"
Height="50" Width="50">
<Rectangle Width="20" Height="20" Fill="{Binding Path=Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}">
<Rectangle.OpacityMask>
<VisualBrush Stretch="Fill" Visual="{DynamicResource appbar_phone}" />
</Rectangle.OpacityMask>
</Rectangle>
</Button>
Didn't like much all of this, but it works. Thanks everyone!
As most people have suggested using a property for CanTalkPrivate is best as you can control when notifications about changes happen. Using Guard methods instead of properties only work well when the parameters are coming from the UI and Caliburn.Micro can monitor them for property changes.
The way I tend to approach a problem like this where you have this sort of view logic a dedicated view model like CallerViewModel and Callers be a collection of these.
public class CallerViewModel : PropertyChangedBase
{
private readonly ICaller caller;
private readonly ICallService callService;
public CallerViewModel(ICaller caller, ICallService callService)
{
this.caller = caller;
this.callService = callService;
}
public void TalkPrivate()
{
callService.TalkPrivate(caller);
}
public bool CanTalkPrivate
{
get
{
return caller.OnHold || callService.IsConference;
}
}
}
You will still need a method of telling this view model that CanTalkPrivate has changed. If ICaller and ICallService use property changed notifications this could work. Of you may need a method on CallerViewModel that you can call when you know things have changed. It's hard to answer that part since I don't know how those two properties are altered, this part is specific to your app.
Edit due to question been updated.
Having ICaller as CallerViewModel certainly makes this solution a lot easier. Adding NotifyOfPropertyChange(() => CanTalkPrviate); to OnHold solves half the problem of correctly updating the UI correctly.
A potential solution for updating the UI on IsConferenceChanging is to one of a few solutions.
Use the event aggregator to broadcast a "call changed message" that CallerViewModel listens for.
Use simple .NET events on ICallService for when the call changes that CallerViewModel subscribes to.
Add code to the methods that use MergeCalls, AddCaller etc to update the CallerViewModel as well.
Caliburn.Micro uses databinding to evaluate when a guard method has to be called.
Implement INotifyPropertyChanged on your ICaller instances and raise PropertyChanged for OnHold and IsConference properties (if you haven't already done that).
Change your method signatures for both TalkPrivate and CanTalkPrivate to have parameters for every value that may change:
public bool CanTalkPrivate(ICaller caller, bool onHold, bool isConference)
{
return onHold || isConference;
}
Change your Action Message accordingly:
TalkPrivate($datacontext, $datacontext.OnHold, $datacontext.IsConference)
Alternatively, you could create an additional property CanTalkPrivate and use that instead:
TalkPrivate($datacontext, $datacontext.CanTalkPrivate)
Or another option is to change it so that the CanTalkPrivate is a bool property and just make a Notification call when you have a change on the underlying service.
public bool CanTalkPrivate
{
get{ return OnHold || IsConference;}
}
NotifyOfPropertyChange(() => CanTalkPrivate);
I am trying to create a wpf combobox containing a list of World of warcraft items. I am using a item template for the combo box so that the items icon and item name are shown in the combobox drop down list. It works as is and I can start typing a item name and the list of items in the popup are automatically filtered.
The problem is that when I click an item I want the item name to be shown in the comboboxes text area. Instead what I get is a very brief ItemDisplayModel.ToString() then a empty string.
The ItemDisplayModel.ToString() being the ItemDisplayModel type that I selected from the list. Which makes sense cause the combobox control ItemsSource property contains an array of ItemDisplayModel types.
But in my AuctionHouseFilterItemNameModel.Value property I am detecting weather the value has changed and if so rebuild the list of items to be displayed in the combobox. So because the Text in the combobox was changed to ItemDisplayModel.ToString() when I click an item in the popup list it again rebuilds the list of items only this time there is no match because ItemDisplayModel.ToString() does not match any known item names.
All I am trying to do is when I click an item in the popup list I want the ItemDisplayModel.Name to be mapped to the AuctionHouseFilterItemNameModel.Value property and have the combobox text area to display AuctionHouseFilterItemNameModel.Value as well.
Does that make sense? I've tried numerous different ways and I am at a loss. Looking around the web did'nt give me the answer either. I'm sure it's something simple to fix but the solution eludes me.
Here is my XAML for the control
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:AuctionHouseSearchFilters="clr-namespace:Codefarts.WowTracks.DataMinerWPF.Models.AuctionHouseSearchFilters"
xmlns:Models="clr-namespace:Codefarts.WowTracks.DataMinerAppCore.Models;assembly=Codefarts.WowTracks.DataMinerAppCore"
xmlns:DataMinerAppCore="clr-namespace:Codefarts.WowTracks.DataMinerAppCore;assembly=Codefarts.WowTracks.DataMinerAppCore"
xmlns:dataMinerWpf="clr-namespace:Codefarts.WowTracks.DataMinerWPF"
x:Name="userControl"
x:Class="Codefarts.WowTracks.DataMinerWPF.Controls.AuctionHouseSearchFilters.AuctionHouseItemNameControl"
mc:Ignorable="d"
d:DesignHeight="51" d:DesignWidth="283">
<UserControl.Resources>
<dataMinerWpf:UriToBitmapImageConverter x:Key="UriToImageConverter" />
<BitmapImage x:Key='defaultImage' UriSource='/Resources\118.png' />
<DataTemplate x:Key="ItemTemplate" DataType="Models:ItemDisplayModel" >
<StackPanel Orientation="Horizontal" Margin="0 5 0 5">
<Image Width="50" Height="50" Stretch="Fill" Source="{Binding IconUrl, Converter={StaticResource UriToImageConverter}, TargetNullValue={StaticResource defaultImage}}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<Label Content="{Binding Name}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<UserControl.DataContext>
<AuctionHouseSearchFilters:AuctionHouseFilterItemNameModel/>
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<ComboBox x:Name="ItemList" MaxDropDownHeight="592" Grid.Row="0" ItemsSource="{Binding ItemDisplayModels}" ItemTemplate="{StaticResource ItemTemplate}" IsEditable="True" IsTextSearchEnabled="True" IsTextSearchCaseSensitive="False" Text="{Binding Value}" >
</ComboBox>
</Grid>
</UserControl>
Here is my code behind
public partial class AuctionHouseItemNameControl : UserControl, IAuctionHouseFilterControl
{
private Application app;
public AuctionHouseItemNameControl()
{
InitializeComponent();
}
public void SetModel(IAuctionHouseSearchFilter model)
{
this.DataContext = model;
}
public void SetApplication(Application app)
{
this.app = app;
}
private void ItemList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//if (model.Handled)
//{
// return;
//}
// model.Handled = true;
if (e.AddedItems != null && e.AddedItems.Count > 0)
{
var model = this.DataContext as Models.AuctionHouseSearchFilters.AuctionHouseFilterItemNameModel;
var item = e.AddedItems[0] as ItemDisplayModel;
// this.ItemList.SelectedValue = item.Name;
model.Value = item.Name;
// this.ItemList.SelectedItem = item.Name;
}
// model.Handled = false;
}
}
and here is my data models
[Export(typeof(IAuctionHouseSearchFilter))]
public class AuctionHouseFilterItemNameModel : IAuctionHouseSearchFilter, INotifyPropertyChanged
{
private string value;
private bool connected;
private Application app;
private ItemDisplayModel[] displayItems;
private CancellationTokenSource backgroundCancel;
private bool buildingDisplayItems;
private readonly object lockObject = new object();
private ItemDisplayModel displayValue;
// private bool Handled { get; set; }
public ItemDisplayModel DisplayValue
{
get
{
return this.displayValue;
}
set
{
if (value == null)
{
return;
}
this.Value = value.Name;
this.displayValue = value;
this.OnPropertyChanged();
}
}
public string Value
{
get
{
return this.value;
}
set
{
//if (this.Handled)
//{
// return;
//}
if (value == this.value)
{
return;
}
this.value = value;
// this.Handled = true;
this.OnPropertyChanged();
var cancellationTokenSource = this.backgroundCancel;
if (cancellationTokenSource != null)
{
cancellationTokenSource.Cancel();
}
this.BuildDisplayItems();
// this.Handled = false;
}
}
public IEnumerable<AuctionDataModel> Filter(MainFilterModel model, IEnumerable<AuctionDataModel> items)
{
if (!this.connected)
{
return items;
}
// get item id from it's name
var list = this.app.ItemNames[model.SelectedRegion];
var id = list.FirstOrDefault(x => x.Name.ToLowerInvariant().Contains(this.value.Trim().ToLowerInvariant()));
return items.Where(x => id != null && x.ItemId == id.Id);
}
public IEnumerable<ItemDisplayModel> ItemDisplayModels
{
get
{
return this.displayItems;
}
}
public async void BuildDisplayItems()
{
if (this.buildingDisplayItems)
{
return;
}
if (!this.connected)
{
this.displayItems = null;
}
// return this.GetDisplayItems();
this.buildingDisplayItems = true;
this.backgroundCancel = new CancellationTokenSource();
this.OnPropertyChanged("ItemDisplayModels");
await Task.Factory.StartNew(
() =>
{
var originalItems = this.displayItems;
this.displayItems = new[] { new ItemDisplayModel() { Name = "Rebuilding list..." } };
var correctedSearchValue = this.value.Trim().ToLowerInvariant();
//lock (this.lockObject)
//{
this.displayItems = (string.IsNullOrWhiteSpace(this.value) ?
this.app.DisplayItemModels :
this.app.DisplayItemModels.Where(x => x.Name.ToLowerInvariant().Contains(correctedSearchValue))).Take(100).AsParallel().ToArray();
this.buildingDisplayItems = false;
this.OnPropertyChanged("ItemDisplayModels");
},
this.backgroundCancel.Token);
}
public string Name
{
get
{
return "Item Name";
}
}
public Type Control
{
get
{
return typeof(AuctionHouseItemNameControl);
}
}
public virtual IAuctionHouseSearchFilter Clone()
{
return new AuctionHouseFilterItemNameModel()
{
Value = this.Value
};
}
public void Connect(Application app)
{
if (this.connected)
{
return;
}
this.app = app;
this.connected = true;
}
public void Disconnect()
{
if (!this.connected)
{
return;
}
this.app = null;
this.connected = false;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class ItemDisplayModel : INotifyPropertyChanged
{
private string iconUrl;
private string region;
private string name;
private int itemId;
public int ItemId
{
get
{
return this.itemId;
}
set
{
if (value == this.itemId)
{
return;
}
this.itemId = value;
this.OnPropertyChanged();
}
}
public string Name
{
get
{
return this.name;
}
set
{
if (value == this.name)
{
return;
}
this.name = value;
this.OnPropertyChanged();
}
}
public string Region
{
get
{
return this.region;
}
set
{
if (value == this.region)
{
return;
}
this.region = value;
this.OnPropertyChanged();
}
}
public string IconUrl
{
get
{
return this.iconUrl;
}
set
{
if (value == this.iconUrl)
{
return;
}
this.iconUrl = value;
this.OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The problem is that when I click an item I want the item name to be shown in the comboboxes text area. Instead what I get is a very brief ItemDisplayModel.ToString() then a empty string.
To get the Name property displayed in the editable text area of the ComboBox use TextSearch.TextPath so your ComboBox definition would look like:
<ComboBox x:Name="ItemList"
Grid.Row="0"
IsEditable="True"
IsTextSearchCaseSensitive="False"
IsTextSearchEnabled="True"
ItemTemplate="{StaticResource ItemTemplate}"
ItemsSource="{Binding ItemDisplayModels}"
MaxDropDownHeight="592"
TextSearch.TextPath="Name" />
Now when an item is selected from the dropdown instead of seeing ItemDisplayModel.ToString(), you will see the Name property.
Hello I have problem with binding data to Listbox. In shortway... I want list all my Skydrive files.
My XAML
<TextBlock Height="35" HorizontalAlignment="Left" Margin="9,6,0,0" Name="infoTextBlock" Text="" VerticalAlignment="Top" Width="Auto" />
<my:SignInButton Name="signInButton1" ClientId="<correct ClientId>" Scopes="wl.signin wl.basic wl.skydrive" Branding="Windows" TextType="SignIn" SessionChanged="signInButton1_SessionChanged" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="198,-6,0,0" />
<StackPanel Height="578" HorizontalAlignment="Left" Margin="10,50,0,0" Name="StackContentPanel" VerticalAlignment="Top" Width="440">
<ListBox Height="465" Name="FileList" Width="380" ItemsSource="{Binding Files}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
My class and cs.
namespace EReader.ViewModel
{
public class File
{
public File()
{}
private string name;
public string Name
{
get { return this.name; }
set { this.name = value; }
}
}
}
public class FilesManager
{
public ObservableCollection<string> Files;
public FilesManager()
{
Files = new ObservableCollection<string>();
}
}
namespace EReader
{
public partial class MainPage : PhoneApplicationPage
{
private LiveConnectClient client;
// Constructor
public MainPage()
{
InitializeComponent();
}
private void signInButton1_SessionChanged(object sender, LiveConnectSessionChangedEventArgs e)
{
if (e.Status == LiveConnectSessionStatus.Connected)
{
client = new LiveConnectClient(e.Session);
infoTextBlock.Text = "Signed in.";
client.GetCompleted +=
new EventHandler<LiveOperationCompletedEventArgs>(OnGetCompleted);
client.GetAsync("/me/skydrive/files/");
}
else
{
infoTextBlock.Text = "Not signed in.";
client = null;
}
}
void OnGetCompleted(object sender, LiveOperationCompletedEventArgs e)
{
//Gdy uda nam się podłaczyc do konta skydrive
if (e.Error == null)
{
signInButton1.Visibility = System.Windows.Visibility.Collapsed;
#region Nazwa użytkownika
string firstName = "";
string lastName = "";
if (e.Result.ContainsKey("first_name") ||
e.Result.ContainsKey("last_name"))
{
if (e.Result.ContainsKey("first_name"))
{
if (e.Result["first_name"] != null)
{
firstName = e.Result["first_name"].ToString();
}
}
if (e.Result.ContainsKey("last_name"))
{
if (e.Result["last_name"] != null)
{
lastName = e.Result["last_name"].ToString();
}
}
infoTextBlock.Text =
"Hello, " + firstName +" "+ lastName + "!";
}
else
{
infoTextBlock.Text = "Hello, signed-in user!";
}
#endregion
#region Wszyskite pliki
List<object> data = (List<object>)e.Result["data"];
FilesManager fileManager = new FilesManager();
foreach (IDictionary<string,object> items in data)
{
File file = new File();
file.Name= items["name"].ToString();
fileManager.Files.Add(file.Name);
}
FileList.ItemsSource = fileManager.Files;
#endregion
}
else
{
infoTextBlock.Text = "Error calling API: " +
e.Error.ToString();
}
}
}
Files must be a property, not a field.
Furthermore {Binding Name} must be {Binding} instead, because a string has no Name property.
This has to be a public property:
public ObservableCollection<string> Files;
should be
public ObservableCollection<string> Files {get;set;}
Then your binding will work