Can´t implicity convert string to viewmodel - wpf

I have a program that should display files in a list view.
My code looks like this and I get error "cannot convert from string into viewmodel" If I change Additem(movies.MovieName) to Additem(movies) I dont get a error but it only writes out "MoviePlayerModule.ViewModelsMoviesViewModel.
I want it to write the file name that it gets from movies.MovieName = filename;
private void ReadMediaFile()
{
var movies = new MoviesViewModel();
string[] files = Directory.GetFiles(Paths.Root);
foreach (var file in files)
{
var filename = Path.GetFileName(file);
movies.MovieName = filename;
};
AddItem(movies.MovieName);
}
In filename it says: "ATTV_bog.mpg" and that is the mpg file thats in the folder.
If I change the code so it looks like this:
var moviee = new MoviesViewModel();
string[] files = Directory.GetFiles(#"C:/Users/v80770/Desktop/Movies");
foreach (var file in files)
{
var filename = file;
moviee.MovieName = filename;
};
AddItem(moviee);
The output just is MoviePlayer.ViewModels.MoviesViewModel
In my View the code is like this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock x:Name="MovieName" Grid.Column="0" />
</Grid>
And I´m using CaliburnMicro.
ViewModel class:
public class MoviesViewModel : TreeViewNode<MoviesViewModel>
{
public string MovieName { get; set; }
}
View:
<UserControl x:Class="ClassName.MovieplayerModule.Views.MoviesView"
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"
mc:Ignorable="d">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock x:Name="MovieName" Grid.Column="0" />
</Grid>
And AddItem method, located in TreeViewBase.cs
public void AddItem(T item)
{
_dispatcher.SmartInvoke(() => Items.Add(item));
}

Add the actual Movie object to the ListView using AddItem(moviee) and then use the DisplayMemberPath property to specify that you want to display the value of the MovieName property:
<ListView ... DisplayMemberPath="MovieName" .... />

Related

Can't access a wpf control from an outside class

I want to call a label control on my main window from an outside class. But the class doesn't recognize it.
My file structure is like this
ZoomBorder.cs
MainWindow.xaml
XAML:
<Window x:Class="GUI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PanAndZoom"
Title="PanAndZoom" Height="600" Width="900" WindowStartupLocation="CenterScreen">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition Height="100"></RowDefinition>
</Grid.RowDefinitions>
<local:ZoomBorder x:Name="border" ClipToBounds="True" Background="Gray">
<!--<Image Source="/GUI;component/Images/Desert.jpg"/>-->
<Canvas Width="300" Height="300" Background="Green"></Canvas>
</local:ZoomBorder>
<StackPanel Grid.Row="1">
<Label x:Name="pos" x:FieldModifier="public">Position</Label>
</StackPanel>
</Grid>
</Window>
ZoomBorder.cs:
public class ZoomBorder : Border
{
...
private void SomeMethod()
{
// this doesn't work!
pos.Content = "This label is changed by ZoomBorder!";
}
}
The way you tried is only works if the property defined in the same class :
pos.Content = "This label is changed by ZoomBorder!";
In case of static property from other class, you can access it this way :
MainWindow.pos = ....
//or generally
ClassName.PropertyName = ....
Unfortunately UI controls in your XAML isn't static property. In case of non static property from other class, you need class instance to access the property :
MainWindow mainWindowInstance = new MainWindow();
mainWindowInstance.pos = ....
Snippet above is only example, in your case you'll need to find a way to pass current MainWindow instance displayed to ZoomBorder instead of creating new instance as this example demonstrates.
There's no reason for it to work. What you're trying to do is pretty much equivalent to :
class Parent // = MainWindow
{
object xxx;
Child child;
}
class Child // = ZoomBorder
{
void SomeFunction()
{
this.xxx = ...; // Doesn't work, the class Child doesn't have any xxx field
}
}
This should work.
var wnd = Application.Current.MainWindow as MainWindow;
var label = wnd.pos;

Displaying elements of ObservableCollection in data grid columns

I have a program where I am reading a .csv file with 2 columns, and my model has 2 properties.
I take instances of this model as the file is read and then add them to my ObvervableCollection.
Here is what it looks like:
// My data field
private ObservableCollection<Model> _internalFile = new ObservableCollection<Model>();
// My data accessor/setter
public ObservableCollection<Model> InternalFile { get { return _internalFile; } set { _internalFile = value; } }
Model x = new Model();
while (fileReader.Peek() != -1)
{
// words = read the line, split the line, make a list of columns
var words = fileReader.ReadLine().Split(',').ToList();
if (words[0] != "Name")
{
x.asWord = words[0];
x.asNumber = int.Parse(words[1]);
InternalFile.Add(x);
// InternalFile is a collection of 'Model' objects.
// So created 'Model' placeholder , x, and pile each instance of x
// in the collection to be displayed.
}
}
My XAML looks like this
<Window x:Class="WpfMVVP.WindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfMVVP"
Title="Window View" Height="350" Width="525" Background="White">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DataGrid Grid.Row="0" AutoGenerateColumns="False" CanUserReorderColumns="False" ItemsSource="{Binding Path=InternalFile}">
<DataGrid.Columns>
<DataGridTextColumn Header="As Word" IsReadOnly="True" Width="Auto" Binding="{Binding asWord}" />
<DataGridTextColumn Header="As Number" IsReadOnly="True" Width="Auto" Binding="{Binding asNumber}" />
</DataGrid.Columns>
</DataGrid>
<Label Grid.Row="1" Content="{Binding Status}" />
</Grid>
In the window displayed, I am seeing the columns populate but all the rows are the last row that was read before end of file. It is as if the ObservableCollection is overwriting its previous elements with a new value. I don't know why it's behaving like that.
You're only creating your Model once, and overwriting it each time w/ the last data read. What you want to do is create a new Model for each line read. You can simply move your Model instantiation inside your while loop. (see below)
// My data field
private ObservableCollection<Model> _internalFile = new ObservableCollection<Model>();
// My data accessor/setter
public ObservableCollection<Model> InternalFile { get { return _internalFile; } set { _internalFile = value; } }
while (fileReader.Peek() != -1)
{
Model x = new Model();
// words = read the line, split the line, make a list of columns
var words = fileReader.ReadLine().Split(',').ToList();
if ((words[0] != "Name") && (!String.IsNullOrEmpty(words[0]))
{
x.asWord = words[0];
x.asNumber = int.Parse(words[1]);
InternalFile.Add(x);
// InternalFile is a collection of 'Model' objects.
// So created 'Model' placeholder , x, and pile each instance of x
// in the collection to be displayed.
}
}

WPF Multi-TextBoxes User Control

I want to have a user control[say UC1] comprising 4 text boxes [say tb1,tb2,tb3, and tb4]. This user control should have 4 normal properties [say prop1, prop2, prop3, and prop4] binding to these text boxes. I want a dependency property [say dp] exposed to outer world by this user control.
This user control gets a single string [say 0\abc|1\def|2\ghi|3\jkl] from a property[say StrProp] of class [say C1] and is splitted into 4 parts[say abc, def, ghi, and jkl] to display in 4 text boxes of my user control. If any changes done by user in any or all textboxes, all the changed texts should be combined and reflected back to class C1\StrProp property.
Also, my requirement is that dp should be bounded to StrProp in UI\XAML. Validations should also be done properly.
Can anyone please help me by writing an example?
Sample classes are as below:
MyMultiTextBoxUserControl.xaml
<UserControl x:Class="MyMultiTextBoxControl_UsingNConsuming.MyMultiTextBoxUserControl"
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"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=".25*"/>
<RowDefinition Height=".25*"/>
<RowDefinition Height=".25*"/>
<RowDefinition Height=".25*"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="{Binding ElementName=UserControl, Path=CombinedField1 }"/>
<TextBox Grid.Row="1" Text="{Binding ElementName=UserControl, Path=CombinedField2}"/>
<TextBox Grid.Row="2" Text="{Binding ElementName=UserControl, Path=CombinedField3}"/>
<TextBox Grid.Row="3" Text="{Binding ElementName=UserControl, Path=CombinedField4}"/>
</Grid>
</UserControl>
MyMultiTextBoxUserControl.xaml.cs
public partial class MyMultiTextBoxUserControl : UserControl
{
public MyMultiTextBoxUserControl()
{
InitializeComponent();
}
//static FrameworkPropertyMetadata propertydata = new FrameworkPropertyMetadata("Hello",
// FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(PropertyChanged_Callback), new CoerceValueCallback(CoerceValue_Callback),
// false, UpdateSourceTrigger.LostFocus);
//public static readonly DependencyProperty CombinedTextProperty =
// DependencyProperty.Register("CombinedText", typeof(string), typeof(MyMultiTextBoxUserControl), propertydata, new ValidateValueCallback(Validate_ValueCallback));
static FrameworkPropertyMetadata propertydata = new FrameworkPropertyMetadata("Hello",
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(PropertyChanged_Callback));
public static readonly DependencyProperty CombinedTextProperty =
DependencyProperty.Register("CombinedText", typeof(string), typeof(MyMultiTextBoxUserControl), propertydata);
private static bool Validate_ValueCallback(object value)
{
string str=value as string;
bool result = true;
if (str.Length > 28)
result = false;
if (str.Length < 1)
result = false;
if (str.Substring(0, 2) != "0'\'")
result = false;
if (str.Contains("1'\'") == false || str.Contains("2'\'") || str.Contains("3'\'"))
result = false;
return result;
}
private static object CoerceValue_Callback(DependencyObject obj,object value)
{
return value;
}
private static void PropertyChanged_Callback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
MyMultiTextBoxUserControl control=(MyMultiTextBoxUserControl)obj;
string select = e.NewValue.ToString();
char[] pipeDelim,slashDelim;
string[] pipeSplt;
pipeDelim = new char[] { '|' };
slashDelim = new Char[] { '/' };
pipeSplt = select.Split(pipeDelim);
if (pipeSplt.Length == 1)
return;
string[][] str = new string[4][];
int x = 0;
foreach (string s in pipeSplt)
{
if (string.IsNullOrEmpty(s) == false)
{
str[x] = s.Split(slashDelim);
x++;
}
}
control.CombinedField1 = str[0][1];
control.CombinedField2 = str[1][1];
control.CombinedField3 = str[2][1];
control.CombinedField4 = str[3][1];
}
public string CombinedText
{
get { return GetValue(CombinedTextProperty) as string; }
set { SetValue(CombinedTextProperty, value); }
}
public string CombinedField1
{
get; set;
}
public string CombinedField2
{
get;
set;
}
public string CombinedField3
{
get;
set;
}
public string CombinedField4
{
get;
set;
}
}
CombinedStringClass.cs
namespace MyMultiTextBoxControl_UsingNConsuming
{
public class CombinedStringClass
{
public CombinedStringClass() { }
string m_CombinedString;
public string CombinedString
{
get { return m_CombinedString; }
set
{
if (m_CombinedString != value)
m_CombinedString = value;
}
}
}
}
ConsumerClass.xaml
<Window x:Class="MyMultiTextBoxControl_UsingNConsuming.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyMultiTextBoxControl_UsingNConsuming;assembly="
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:CombinedStringClass x:Key="myClass"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*"/>
<ColumnDefinition Width="0.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.33*"/>
<RowDefinition Height="0.34*"/>
<RowDefinition Height="0.33*"/>
</Grid.RowDefinitions>
<TextBlock Text="User Control Text Boxes" Grid.Row="0" Grid.Column="0" Foreground="Black" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<local:MyMultiTextBoxUserControl Grid.Row="0" Grid.Column="1" Foreground="Black" CombinedText="{Binding Source=myClass, Path=CombinedString, Mode=TwoWay,FallbackValue=DataNotBound}"/>
<TextBlock Text="Combied String" Grid.Row="2" Grid.Column="0" Foreground="Black" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<TextBox Name="OneStringTextBox" Grid.Row="2" Grid.Column="1" Foreground="Black" Text="0\abc|1\def|2\ghi|3\jkl" IsEnabled="False"/>
</Grid>
</Window>
I also need to combine the changed texts ofUserControl's textboxes in such a way that it should be in a form of [0\f|1\gh|2\zx|3\oo] to be reflected in OneStringTextBox. Also, total string length should be 28 & max length of each textbox is 7.
Read WPF in C# 2010: Windows Presentation Foundation in .NET 4 Matthew MacDonald Chapter 18.
There is a great example that shoud help you.
Give name to your User control, replace {Binding ElementName=UserControl... with {Binding ElementName=NameOfUserControl, convert CombinedFields properties to DPs.

How do I bind arrays to columns in a WPF datagrid

I have a Log object that contains a list of Curve objects. Each curve has a Name property and an array of doubles. I want the Name to be in the column header and the data below it. I have a user control with a datagid. Here is the XAML;
<UserControl x:Class="WellLab.UI.LogViewer"
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"
mc:Ignorable="d"
d:DesignHeight="500" d:DesignWidth="500">
<Grid>
<StackPanel Height="Auto" HorizontalAlignment="Stretch" Margin="0" Name="stackPanel1" VerticalAlignment="Stretch" Width="Auto">
<ToolBarTray Height="26" Name="toolBarTray1" Width="Auto" />
<ScrollViewer Height="Auto" Name="scrollViewer1" Width="Auto" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Visible" CanContentScroll="True" Background="#E6ABA4A4">
<DataGrid AutoGenerateColumns="True" Height="Auto" Name="logDataGrid" Width="Auto" ItemsSource="{Binding}" HorizontalAlignment="Left">
</DataGrid>
</ScrollViewer>
</StackPanel>
</Grid>
In the code behind I have figured out how to create columns and name them, but I have not figured out how to bind the data.
public partial class LogViewer
{
public LogViewer(Log log)
{
InitializeComponent();
foreach (var curve in log.Curves)
{
var data = curve.GetData();
var col = new DataGridTextColumn { Header = curve.Name };
logDataGrid.Columns.Add(col);
}
}
}
I wont even show the code I tried to use to bind the array "data", since nothing even came close. I am sure I am missing something simple, but after hours of searching the web, I have to come begging for an answer.
You need to pivot the log data into a collection of RowDataItem where each row contains a collection of double values, one for each Curve. At the same time you can extract the column names. So the data would end up like this.
public class PivotedLogData : ViewModelBase
{
public PivotedLogData(Log log)
{
ColumnNames = log.Curves.Select(c => c.Name).ToList();
int numRows = log.Curves.Max(c => c.Values.Count);
var items = new List<RowDataItem>(numRows);
for (int i = 0; i < numRows; i++)
{
items.Add(new RowDataItem(
log.Curves.Select(
curve => curve.Values.Count > i
? curve.Values[i]
: (double?) null).ToList()));
}
Items = items;
}
public IList<string> ColumnNames { get; private set; }
public IEnumerable<RowDataItem> Items { get; private set; }
}
public class RowDataItem
{
public RowDataItem(IList<double?> values)
{
Values = values;
}
public IList<double?> Values { get; private set; }
}
Then you would create DataGridTextColumn items as above but with a suitable binding
var pivoted = new PivotedLogData(log);
int columnIndex = 0;
foreach (var name in pivoted .ColumnName)
{
dataGrid.Columns.Add(
new DataGridTextColumn
{
Header = name,
Binding = new Binding(string.Format("Values[{0}]", columnIndex++))
});
}
Now bind the data to the grid
dataGrid.ItemsSource = pivoted.Items;

Two dynamically assigned ContentControls in single view in Caliburn.Micro

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.

Resources