As I'm developing my app, I'm finding that I'm re-creating a "tile" control far too often. Therefore I'm currently trying to move it into a User Control for re-use. However, it's currently not accepting any bindings that were previously working. So for example:
<Canvas Height="73" Width="73" VerticalAlignment="Top" Margin="10,10,8,0">
<Rectangle Height="73" Width="73" VerticalAlignment="Top" Fill="{Binding Path=Active, Converter={StaticResource IconBackground}}" />
<Image Height="61" Width="61" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="6" Source="{Binding Tone.Image}" />
</Canvas>
Works fine with the bindings,
<views:Tile Height="73" Width="73" Background="{Binding Path=Active, Converter={StaticResource IconBackground}, Mode=OneWay}" Icon="{Binding Path=Tone.Image, Mode=OneTime}" />
produces the error "the parameter is incorrect".
Here is the code for my Tile UserControl:
Tile.xaml
<UserControl x:Class="RSS_Alarm.Views.Tile"
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"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="100" d:DesignWidth="100">
<Grid x:Name="LayoutRoot">
<Canvas Height="100" Width="100" Margin="0,0,0,0">
<Rectangle Name="rectBackground" Height="100" Width="100" />
<Image Name="imgIcon" Height="80" Width="80" VerticalAlignment="Center" HorizontalAlignment="Center" Canvas.Left="10" Canvas.Top="10" />
</Canvas>
</Grid>
</UserControl>
Tile.xaml.cs
namespace RSS_Alarm.Views
{
public partial class Tile : UserControl
{
public Tile()
{
InitializeComponent();
}
public String Icon
{
get
{
return imgIcon.Source.ToString();
}
set
{
BitmapImage alarmIcon = new BitmapImage();
alarmIcon.UriSource = new Uri(value, UriKind.Relative);
imgIcon.Source = alarmIcon;
}
}
new public Brush Background
{
get
{
return rectBackground.Fill;
}
set
{
rectBackground.Fill = value;
}
}
new public double Height
{
get
{
return rectBackground.Height;
}
set
{
rectBackground.Height = value;
imgIcon.Height = value * 0.8;
}
}
new public double Width
{
get
{
return rectBackground.Width;
}
set
{
rectBackground.Width = value;
imgIcon.Width = value * 0.8;
}
}
}
}
If you need any more source, let me know and I'll post it. I don't have any problems when using a fixed value (Height and Width are fine, and if I set Background to Red then that also works fine), but changing to a Binding value throws the exception.
EDIT 1
Here's some updated code:
Tile.xaml.cs
#region Background
public static readonly DependencyProperty RectBackgroundProperty =
DependencyProperty.Register(
"RectBackground",
typeof(SolidColorBrush),
typeof(Tile),
new PropertyMetadata(new SolidColorBrush(Colors.Green), new PropertyChangedCallback(OnBackgroundChanged))
);
public static void OnBackgroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("Filling background");
((Tile)d).rectBackground.Fill = (Brush)e.NewValue;
}
new public SolidColorBrush Background
{
get { return (SolidColorBrush)GetValue(RectBackgroundProperty); }
set {
Debug.WriteLine("Setting colour");
SetValue(RectBackgroundProperty, value);
}
}
#endregion
MainMenuControl.xaml.cs
// Class to determine the background colour of the icon (active/inactive)
public class IconBackground : System.Windows.Data.IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool b = (bool)value;
Debug.WriteLine("Converting colour. Value is " + b.ToString());
if (b)
{
return (Brush)App.Current.Resources["PhoneAccentBrush"];
}
else
{
return new SolidColorBrush(Colors.DarkGray);
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
SolidColorBrush brush = (SolidColorBrush)value;
if (brush.Color.Equals(Colors.DarkGray))
{
return false;
}
else
{
return true;
}
}
}
I'm also comparing the two methods side-by-side. The tile on the left is the defined Canvas with bindings fully working, while the tile on the right is the Tile UserControl, which only works with defined colours (Blue in this case)
In order to be able to bind in XAML it is not enough to make a property. You have to create a DependencyProperty.
The reason your Background binding works, is that UserControl itself has this property. If you set a breakpoint in your Background property setter, you will see that it is never called.
Here is an example of a DependencyProperty for your Background (not tested)
#region Background
public const string BackgroundPropertyName = "Background";
public new Brush Background
{
get { return (Background)GetValue (BackgroundProperty); }
set { SetValue (Background, value); }
}
public static new readonly DependencyProperty BackgroundProperty = DependencyProperty.Register (
BackgroundPropertyName,
typeof (Brush),
typeof (Tile),
new PropertyMetadata (BackgroundChanged));
static void BackgroundChanged (DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((Tile) d).rectBackground = (Brush)e.NewValue;
}
#endregion
Related
Enable button_A when button_B is enabled and image source has a specific .png icon
I have two Buttons and an Image object in a WPF application built with .NET Core and C#. What I want on the bottom line is to enable Button_A only when the Button_B is enabled and the Image has a specific .png icon of a checkmark. For those three objects an MVVM model exists. More details in the code below,
XAML file
<Window x:Class="MyApp.MainWindow"
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"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:local="clr-namespace:MyApp"
mc:Ignorable="d"
Height="1080"
Width="1920"
ResizeMode="NoResize">
<Grid x:Name="MyGrid"
Background="White"
HorizontalAlignment="Center"
ShowGridLines="False">
<!--Grid Columns-->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<!--Grid Rows-->
<Grid.RowDefinitions>
<RowDefinition Height="45"/>
<RowDefinition Height="45"/>
</Grid.RowDefinitions>
<Button
x:Name="Button_A"
Click="Button_A_Click"
Content="Execute"
IsEnabled="{Binding EnableButtonA}"
HorizontalAlignment="Left"
Width="80"
Height="25"
Margin="135,0,0,0"
Grid.Column="0"
Grid.Row="0"/>
<Button
x:Name="Button_B"
Click="Button_B_Click"
Content="Execute"
IsEnabled="{Binding EnableButtonB}"
HorizontalAlignment="Left"
Width="80"
Height="25"
Margin="135,0,0,0"
Grid.Column="1"
Grid.Row="1">
</Button>
<Image
x:Name="IconSymbol"
Source="{Binding Path=ImageChangeSource,UpdateSourceTrigger=PropertyChanged}"
Grid.Row="1"
Grid.Column="1"
Width="{Binding Path=CalculationsImageWidth}"
Height="Auto"
HorizontalAlignment="Left"
Margin="190,0,0,0"
Visibility="Visible"
IsEnabled="True"/>
</Grid>
</Window>
.CS file - MVVM model
namespace MyApp
{
public class CustomViewModel : INotifyPropertyChanged
{
//Button B
private bool _enableButtonB;
public bool EnableButtonB
{
get
{
return _enableButtonB;
}
set
{
_enableButtonB = value;
OnPropertyChanged("EnableButtonB");
}
}
//Image
private ImageSource _imageChangeSource;
public ImageSource ImageChangeSource
{
get
{
return _imageChangeSource;
}
set
{
_imageChangeSource = value;
OnPropertyChanged("ImageChangeSource");
}
}
//Image width
private int _changeImageWidth;
public int ImageWidth
{
get
{
return _changeImageWidth;
}
set
{
_changeImageWidth= value;
OnPropertyChanged("ImageWidth");
}
}
//Button A
private bool _enableButtonA;
public bool EnableButtonA
{
get
{
return _enableButtonA;
}
set
{
//What to write here?
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string property)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
What I tried so far is based on this similar question I asked in the past. A relevant answer posted in the attached question is the use of IMultiValueConverter. However, I am not confident to figure out how to properly use the Converter for my task.
(The code below won't work)
public class EnableReportConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return ((bool)values[0]=true, (ImageSource)values[1]=new BitmapImage(new Uri("/Assets/checkmark.png", UriKind.Relative)));
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
<Button
x:Name="Button_A"
Click="Button_A_Click"
Content="Execute"
HorizontalAlignment="Left"
Width="80"
Height="25"
Margin="135,0,0,0"
Grid.Column="0"
Grid.Row="0">
<Button.IsEnabled>
<MultiBinding>
<MultiBinding.Converter>
<local:EnableReportConverter/>
</MultiBinding.Converter>
<Binding Path="EnableButtonB"/>
<Binding Path="ImageChangeSource"/>
</MultiBinding>
</Button.IsEnabled>
</Button>
[EDIT]--example of an ICommand
public ICommand ButtonACommand
{
get { return new DelegateCommand<object>(FuncBrowseFileCommand); }
}
public void FuncBrowseFileCommand(object parameters)
{
var final_result = BrowseFile(FilesFilePath);
Nullable<bool> browse_result = final_result.browse_result;
FilesFilePath = final_result.filename;
//below are some MVVM object-- dont pay them attention
if (browse_result == true)
{
EnableFilesLoadButton = true;
EnableFilesBrowseButton = true;
EnableFilesViewButton = false;
FilesPanelVisibility = false;
}
else
{
FilesImageVisibility = true;
return;
}
}
Better approach:
I understand "WHAT" you need but , I am not sure "WHY" you need this. There are better ways to enable a button. I also notice that you are using button click which is obviously code behind. When you have MVVM model, try to use ICommand and attach to the "command" property of the button. If you do that , then you can easily assign a delegate to "CanExecute" to make the enabling of the button.
Solution to current problem: Regardless of the above suggestion, solution to your current problem is as below.
The below line in your converter is wrong. This returns object[] again. Basically, you receive an array from the XAML and return the same again. You need to receive the array, process it and return a result (which is "bool" in your case : to enable a button).
return ((bool)values[0]=true, (ImageSource)values[1]=new BitmapImage(new Uri("/Assets/checkmark.png", UriKind.Relative)));
So, do the validation like below..
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
//INCOMING DATA
bool is_buttonB_enabled = (bool)values[0]; //This is the first value in the object array.
ImageSource _image = (ImageSource) values[1]; //This is the second value in the object array.
//YOUR EXPECTED IMAGE.
ImageSource _expected_image = new BitmapImage(new Uri("/Assets/checkmark.png", UriKind.Relative));
//VALIDATE
return (is_buttonB_enabled == true && _image == _expected_image );
}
UPDATE:
In case, you use an ICommand (and return a delegatecommand), then you can follow below approach.
public ICommand ButtonACommand
{
get { return new DelegateCommand<object>(FuncBrowseFileCommand,_canEnableButton); }
}
private bool _canEnableButton(object obj)
{
//YOUR EXPECTED IMAGE.
ImageSource _expected_image = new BitmapImage(new Uri("/Assets/checkmark.png", UriKind.Relative));
return (EnableButtonB == true && ImageChangeSource == _expected_image );
}
then, you don't need converter..
i have a problem converting a string to a icon. The icon Geometry is in a ResourceDictionary. The ValueConverter is not called (i tried to debug in the Convert Method of the Converter). Here is my code:
xaml:
<Window.Resources>
<local:StatusToPathDataConverter x:Key="PathConverter"/>
</Window.Resources>
<Grid>
<Path Width="20"
Height="20"
Stretch="Uniform"
Fill="Black"
Data="{Binding Path=Status,
UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource PathConverter}}"/>
</Grid>
cs:
public partial class MainWindow :Window {
public MainWindow() {
InitializeComponent();
}
public string Status
{
get { return (string)GetValue(StatusProperty); }
set { SetValue(StatusProperty, value); }
}
public static readonly DependencyProperty StatusProperty =
DependencyProperty.Register("Status", typeof(string), typeof(MainWindow));
}
public class StatusToPathDataConverter :IValueConverter {
private static ResourceDictionary iconDictionary;
public ResourceDictionary IconDictionary
{
get
{
if(iconDictionary == null) {
iconDictionary = new ResourceDictionary();
iconDictionary.Source = new Uri("/WPFBindingTest;component/Resources/IconDictionary.xaml", UriKind.RelativeOrAbsolute);
}
return iconDictionary;
}
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
var status = (string)value;
if(statinStatus == null)
return null;
switch(status.ToLower()) {
case "test":
return IconDictionary["TestIcon"];
// ...
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
You're not binding to anything. You need to tell the Binding to go to the Window to find the Status property. The recommended way to do that is to use RelativeSource, as below:
<Path
Width="20"
Height="20"
Stretch="Uniform"
Fill="Black"
Data="{Binding Path=Status,
RelativeSource={RelativeSource AncestorType=Window},
Converter={StaticResource PathConverter}}"
/>
As #Clemens notes, UpdateSourceTrigger=PropertyChanged doesn't make any sense on this binding and should not be there. That attribute tells the Binding when it should update the binding's source property. The source property is Window.Status, in this case.
However, the Path.Data property does not update the property it's bound to. A Path displays a Geometry; it doesn't edit a Geometry. UpdateSourceTrigger exists for control properties that update viewmodel properties, like TextBox.Text. That's the most common use for UpdateSourceTrigger=PropertyChanged: By default TextBox.Text updates the source property when the TextBox loses focus, but sometimes you want it to update on each keystroke.
Set the DataContext of the window to itself for the binding to work and the Convert method of the converter to get called:
public MainWindow() {
InitializeComponent();
DataContext = this;
}
If the binding to the source property fails the converter will never be invoked.
In the code below, I have a UserControl that contains an ellipse and a textblock. I'd like to create a reusable control that I can bind to that allows me to set the text based on a string, and changes the fill color between Red/Green based on a boolean.
I can do this now by digging deep into the markup and using some complex binding, but I want to reuse this control in a list and it seemed easier to create a control for the purpose. However, I am not sure where to go next and if I should be creating dependency-properties that tie to the values for Fill and Text, or what.
<UserControl
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"
x:Class="Herp.Derp.View.DeliveryStatusIndicator"
x:Name="UserControl"
d:DesignWidth="91" d:DesignHeight="35">
<Grid x:Name="LayoutRoot">
<StackPanel Orientation="Horizontal">
<Ellipse Width="35" Height="35" Fill="Green">
<Ellipse.OpacityMask>
<VisualBrush Stretch="Fill" Visual="{StaticResource appbar_location_circle}"/>
</Ellipse.OpacityMask>
</Ellipse>
<TextBlock Style="{StaticResource Heading2}"
VerticalAlignment="Center" Margin="3,0,0,0">
<Run Text="FRONT"/>
</TextBlock>
</StackPanel>
</Grid>
</UserControl>
here how you can achieve this
UserControl
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public static readonly DependencyProperty FrontTextProperty = DependencyProperty.Register( "FrontText", typeof(string),typeof(UserControl1), new FrameworkPropertyMetadata(string.Empty));
public string FrontText
{
get { return (string)GetValue(FrontTextProperty); }
set {
SetValue(FrontTextProperty, value);
frontBlock.Text = value;
}
}
public static readonly DependencyProperty EllipseStateProperty = DependencyProperty.Register("EllipseState", typeof(bool), typeof(UserControl1), new FrameworkPropertyMetadata(false));
public bool EllipseState
{
get { return (bool)GetValue(EllipseStateProperty); }
set
{
SetValue(EllipseStateProperty, value);
if (value)
{
ellipse.Fill = new SolidColorBrush( Colors.Green);
}
else
{
ellipse.Fill = new SolidColorBrush(Colors.Red);
}
}
}
}
MainWindow
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1" x:Class="WpfApplication1.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:UserControl1 EllipseState="{Binding yourProperty }"/>
<CheckBox Content="CheckBox" HorizontalAlignment="Left" Margin="207,94,0,0" VerticalAlignment="Top"/>
</Grid>
</Window>
Yes, to create properties that "parent" XAML can assign bindings to, you need to create aDependencyProperty for each field that you want to bind to.
You would then bind your user control xaml to the backing property for the DP.
Here's what I got for a working solution:
public partial class DeliveryStatusIndicator : UserControl
{
public DeliveryStatusIndicator()
{
InitializeComponent();
}
public static readonly DependencyProperty DeliveryDescriptionProperty = DependencyProperty.Register("DeliveryDescription", typeof(string), typeof(DeliveryStatusIndicator), new FrameworkPropertyMetadata("Default", DescriptionChangedEventHandler));
private static void DescriptionChangedEventHandler(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((DeliveryStatusIndicator)d).Desc.Text = (string)e.NewValue;
}
public string DeliveryDescription
{
get { return (string)GetValue(DeliveryDescriptionProperty); }
set
{
SetValue(DeliveryDescriptionProperty, value);
}
}
public static readonly DependencyProperty DeliveryStatusProperty = DependencyProperty.Register("DeliveryStatus", typeof(bool), typeof(DeliveryStatusIndicator), new FrameworkPropertyMetadata(false, StatusChangedEventHandler));
private static void StatusChangedEventHandler(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((DeliveryStatusIndicator)d).Indicator.Fill = (bool)e.NewValue ? new SolidColorBrush(Colors.Green) : new SolidColorBrush(Colors.Red);
}
public bool DeliveryStatus
{
get { return (bool)GetValue(DeliveryStatusProperty); }
set
{
SetValue(DeliveryStatusProperty, value);
}
}
}
I bind the Rectangle Fill property on bool value (Fill="{Binding Path=IsSelected, Converter={StaticResource rectangleFillConverter}}") and it throws a null exception. I checked the value of property (IsSelected) is not null. When I remove Converter from Fill property it works. Here is my code:
xaml
<Rectangle Width="{Binding Duration}" Height="20" Tag="{Binding .}" IsEnabled="{Binding Path=IsEnabled}" Fill="{Binding Path=IsSelected, Converter={StaticResource rectangleFillConverter}}" ToolTip="{Binding Path=Shift}" MouseDown="LabelShift_MouseDown"></Rectangle>
Converter
public class RectangleControlFillConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
SolidColorBrush brush;
bool b= (bool)value;
if (b)
brush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#5C8FFF"));
else
brush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#73E34D"));
return brush;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Static Resource
<converters:RectangleControlFillConverter x:Key="rectangleFillConverter"/>
Property
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
OnPropertyChanged("IsSelected");
}
}
ItemsControl where is rectangle and this converter works Converter={StaticResource timeToPositionConverter}}"
<ItemsControl Name="icSchedule" ItemsSource="{Binding .}" Grid.Row="1" Grid.Column="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Margin="0"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Path=icw}" Tag="{Binding .}" Margin="0,10,0,0"><!--Margin="3"Grid.Row="1" Grid.Column="1" Background="DarkGray" BorderThickness="1" BorderBrush="White"-->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="Canvas.Left" Value="{Binding Path=Start, Converter={StaticResource timeToPositionConverter}}" />
<Setter Property="Canvas.Top" Value="{Binding Path=Index}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="TimeLineEntry">
<Border BorderThickness="1" BorderBrush="DarkGray">
<Rectangle Width="{Binding Duration}" Height="20" Tag="{Binding .}" IsEnabled="{Binding Path=IsEnabled}" Fill="{Binding Path=IsSelected, Converter={StaticResource rectangleFillConverter}}" ToolTip="{Binding Path=Shift}" MouseDown="LabelShift_MouseDown">
</Rectangle>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This is the class on wich object is rectangle binding (icw is a list of that objects)
public partial class ScheduleItem
{
public string Shift
{
get
{
//string s = ((DateTime)DateFrom).ToString("dd.MM.yyyy") + " " + ((TimeSpan)TimeFrom).ToString() + " - " + ((DateTime)DateTo).ToString("dd.MM.yyyy") + " " + ((TimeSpan)TimeTo).ToString();
String s = String.Format("{0}.{1} {2}:{3} - {4}.{5} {6}:{7}", FullDateFrom.Day, FullDateFrom.Month, FullDateFrom.Hour, FullDateFrom.Minute, FullDateTo.Day, FullDateTo.Month, FullDateTo.Hour, FullDateTo.Minute);
return s;
}
}
private DateTime FullDateFrom
{
get
{
DateTime dt = ((DateTime)DateFrom).AddHours(((TimeSpan)TimeFrom).Hours).AddMinutes(((TimeSpan)TimeFrom).Minutes);
return dt;
}
}
private DateTime FullDateTo
{
get
{
DateTime dt = ((DateTime)DateTo).AddHours(((TimeSpan)TimeTo).Hours).AddMinutes(((TimeSpan)TimeTo).Minutes);
return dt;
}
}
public string Name { get; set; }
public DateTime Start { get { return FullDateFrom; } }
private int index;
public int Index
{
get
{
if (CampaignPerson != null)
return CampaignPerson.Index;
else
return index;
}
set
{
index = value;
}
}
public int Duration
{
get
{
TimeSpan dt = FullDateTo - FullDateFrom;
return (dt.Days* 92) + (dt.Hours*4);
}
}
public bool IsEnabled
{
get { return (FullDateFrom > DateTime.Now); }
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
OnPropertyChanged("IsSelected");
}
}
#region setters
partial void OnTimeFromChanged()
{
OnPropertyChanged("Duration");
OnPropertyChanged("Start");
}
partial void OnTimeToChanged()
{
OnPropertyChanged("Duration");
OnPropertyChanged("Start");
}
partial void OnDateFromChanged()
{
OnPropertyChanged("Duration");
OnPropertyChanged("Start");
}
partial void OnDateToChanged()
{
OnPropertyChanged("Duration");
OnPropertyChanged("Start");
}
#endregion
}
ViewModel Class The icShedule is Binding on collection of this Class
public class ScheduleExtension
{
public ICollectionView icw {get; set;}
public ScheduleExtension(CampaignPerson cp)
{
campainPerson = cp;
scheduleItemsList.CollectionChanged += new NotifyCollectionChangedEventHandler(_scheduleItemsList_CollectionChanged);
icw = CollectionViewSource.GetDefaultView(scheduleItemsList);
icw.Filter = ScheduleFilter;
}
private CampaignPerson _campainPerson;
public CampaignPerson campainPerson
{
get { return _campainPerson; }
set
{
_campainPerson = value;
scheduleItemsList = new ObservableCollection<ScheduleItem>(_campainPerson.ScheduleItem.Where(p=>p.active));
}
}
private DateTime _currentDate;
public DateTime CurrentDate
{
get { return _currentDate; }
set
{
_currentDate = value;
icw.Refresh();
//CurrrentScheduleItemsList = new ObservableCollection<ScheduleItem>(scheduleItemsList.Where(p => p.active && ((DateTime)p.DateFrom).Month == CurrentDate.Month && ((DateTime)p.DateFrom).Year == CurrentDate.Year));
//OnPropertyChanged("CurrentScheduleItemsList");
}
}
public ObservableCollection<ScheduleItem> scheduleItemsList;
void _scheduleItemsList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
}
private bool ScheduleFilter(object item)
{
if (CurrentDate != null)
{
ScheduleItem si = item as ScheduleItem;
return (CurrentDate.Month == ((DateTime)si.DateFrom).Month && CurrentDate.Year == ((DateTime)si.DateFrom).Year);
}
return false;
}
}
Try changing the <DataTemplate DataType="TimeLineEntry"> to <DataTemplate DataType="ScheduleItem">. Maybe ScheduleItem is a TimeLineEntry (you posted only one part of a partial class), but try if it works.
The problem could also be caused by the way you are handling the collection. In the constructor you say:
icw = CollectionViewSource.GetDefaultView(scheduleItemsList);
icw.Filter = ScheduleFilter;
And then in the campainPerson property you say:
scheduleItemsList = new ObservableCollection<ScheduleItem>(_campainPerson.ScheduleItem.Where(p=>p.active));
You have to either use the same collection object (by clearing it and adding new items), or by creating new ICollectionView and assigning it to icw (so you'd have to repeat the two lines from constructor after creating new ObservableCollection). Try that too.
I dont think problem is from converter, even if all work fine when you remove it. This is my test app.
MainWindow.xaml
<Window x:Class="MVVMTests.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"
xmlns:local="clr-namespace:MVVMTests"
Background="{StaticResource {x:Static SystemColors.ControlBrushKey}}"
>
<Window.Resources>
<ResourceDictionary>
<local:RectangleControlFillConverter x:Key="rectangleFillConverter" />
</ResourceDictionary>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<CheckBox Content="toto" IsChecked="{Binding IsSelected}" />
<Rectangle Fill="{Binding Path=IsSelected, Converter={StaticResource rectangleFillConverter}}" Grid.Row="1" />
</Grid>
</Window>
The ViewModel
namespace MVVMTests
{
public class MainWindowViewModel : NotificationObject
{
private Boolean isSelected;
public MainWindowViewModel()
{
isSelected = true;
}
public Boolean IsSelected
{
get { return this.isSelected; }
set
{
this.isSelected = value;
this.RaisePropertyChanged<Boolean>(() => IsSelected);
}
}
}
}
and the conveter
namespace MVVMTests
{
[ValueConversion(typeof(Boolean), typeof(SolidColorBrush))]
public class RectangleControlFillConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
SolidColorBrush brush;
bool b = (bool)value;
if (b)
brush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#5C8FFF"));
else
brush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#73E34D"));
return brush;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
With this simple code, we can see that the converter is not the problem (window switch ugly blue / ugly green), and binding works fine.
That's not a solution, but that's show us the problem is elsewhere ...
Converter in rectangle still doesnt work on any property.
This is working for me Fill="{Binding Path=CurrentColor}"
and this change in ScheduleItem
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
if(_isSelected)
CurrentColor = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#73E34D"));
else
CurrentColor = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#5C8FFF"));
OnPropertyChanged("CurrentColor");
}
}
private SolidColorBrush _currentColor = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#5C8FFF"));
public SolidColorBrush CurrentColor
{
get { return _currentColor; }
set { _currentColor = value; }
}
Thanks guys for your time and your suggestion.
I'm trying to position elements in my Canvas relative to my background.
Window is re-sized keeping the aspect ratio.
Background is stretched with window size.
The problem is once window is re-sized the element positions are incorrect. If window is re-sized just a little, elements will adjust their size a bit and would be still in the correct position, but if window is re-sized to double it's size then positioning is completely off.
So far I used Grid, but it was to no avail as well. Here is the XAML
<Window x:Class="CanvasTEMP.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" ResizeMode="CanResizeWithGrip" SizeToContent="WidthAndHeight" MinHeight="386" MinWidth="397.5" Name="MainWindow1"
xmlns:c="clr-namespace:CanvasTEMP" Loaded="onLoad" WindowStartupLocation="CenterScreen" Height="386" Width="397.5" WindowStyle="None" AllowsTransparency="True" Topmost="True" Opacity="0.65">
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas Height="77" Width="218">
<Label Content="{Binding OwnerData.OwnerName}" Height="36" Canvas.Left="8" Canvas.Top="55" Width="198" Padding="0" HorizontalAlignment="Left" HorizontalContentAlignment="Center" VerticalAlignment="Center"/>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas>
<Canvas.Background>
<ImageBrush ImageSource="Resources\default_mapping.png" Stretch="Uniform"/>
</Canvas.Background>
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding OwnerData.left}" />
<Setter Property="Canvas.Top" Value="{Binding OwnerData.top}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
Class that is used for data binding
public class Owner : INotifyPropertyChanged
{
public double _left;
public double _top;
public string OwnerName { get; set; }
public double top { get { return _top; }
set
{
if (value != _top)
{
_top = value;
OnPropertyChanged();
}
}
}
public double left
{
get { return _left; }
set
{
if (value != _left)
{
_left = value;
OnPropertyChanged();
}
}
}
public string icon { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "none passed")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class ForDisplay
{
public Owner OwnerData { get; set; }
public int Credit { get; set; }
}
And here is the code that is run every second to keep elements' position relative to window size
items[0].OwnerData.left = this.Width * (10 / Defaul_WindowSize_Width);
items[0].OwnerData.top = this.Height * (55 / Defaul_WindowSize_Height);
10 and 50 are default Canvas.Left and Canvas.Top that are used when window is first initialized.
Would appreciate if anyone can point out what I'm doing wrong.
You need an attach property:
public static readonly DependencyProperty RelativeProperty =
DependencyProperty.RegisterAttached("Relative", typeof(double), typeof(MyControl));
public static double GetRelative(DependencyObject o)
{
return (double)o.GetValue(RelativeProperty);
}
public static void SetRelative(DependencyObject o, double value)
{
o.SetValue(RelativeProperty, value);
}
and a converter:
public class RelativePositionConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var rel = (double)values[0];
var width = (double)values[1];
return rel * width;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and when you add child to Canvas you need like this:
var child = new Child();
SetRelative(child, currentPosition / ActualWidth);
var multiBinding = new MultiBinding { Converter = new RelativePositionConverter() };
multiBinding.Bindings.Add(new Binding { Source = child, Path = new PropertyPath(RelativeProperty) });
multiBinding.Bindings.Add(new Binding { Source = canvas, Path = new PropertyPath(ActualWidthProperty) });
BindingOperations.SetBinding(child, LeftProperty, multiBinding);
Children.Add(child);
If you need, you can change Relative value of child separate of Canvas