mardi 28 octobre 2014

Re-use different storyboards in wpf for multiple but alike controls, MVVM


Vote count:

0




I want to display 3 candles being blown out and need to animate their current state depending on the ratio of an incoming value and a reference value. The ratio is calculated by my CandleAnimationViewModel, which holds all the CandleVMs in a Collection and then set for each CandleVM


My CandleViewModel



/// <summary>
/// </summary>
public class CandleViewModel : ViewModelAbstract
{
/// <summary>
/// Initializes a new instance of the <see cref="CandleViewModel"/> class.
/// </summary>
public CandleViewModel()
{
this.CandleAnimationState = CandleAnimationState.Idle;
}

/// <summary>
/// Gets or sets the candle animation state.
/// </summary>
public CandleAnimationState CandleAnimationState
{
get
{
return this.GetPropertyValue<CandleAnimationState>();
}

set
{
if (this.GetPropertyValue<CandleAnimationState>() != CandleAnimationState.Dead)
{
this.SetPropertyValue(value);
this.OnAnimationStateChanged(value);
}
}
}

/// <summary>
/// </summary>
/// <param name="candleAnimationState">
/// The candle animation state.
/// </param>
/// <exception cref="ArgumentException">
/// </exception>
private void OnAnimationStateChanged(CandleAnimationState candleAnimationState)
{
switch (candleAnimationState)
{
case CandleAnimationState.Idle:
break;
case CandleAnimationState.LowFlicker:
break;
case CandleAnimationState.HighFlicker:
break;
case CandleAnimationState.Dead:
break;
default:
throw new ArgumentException(
"No such state known to animate, state: {1}",
candleAnimationState.ToString());
}
}
}


MY CandleAnimationViewModel



public class CandleAnimationViewModel : ViewModelAbstract
{
private readonly CandleViewModel firstCandle = new CandleViewModel();
private readonly CandleViewModel secondCandle = new CandleViewModel();
private readonly CandleViewModel thirdCandle = new CandleViewModel();

private ProcessingCollection<double> AirPressureData;

private EventSubscriptionManager eventSubscriptionManager;

private bool isDisposed;

private double? referenceAirPressure;

/// <summary>
/// Initializes a new instance of the <see cref="CandleAnimationViewModel"/> class.
/// </summary>
/// <param name="eventSubscriptionManager">
/// The event Subscription Manager.
/// </param>
/// <param name="airPressureData">
/// The adjusted data.
/// </param>
public CandleAnimationViewModel(
EventSubscriptionManager eventSubscriptionManager,
ProcessingCollection<double> airPressureData)
{
this.eventSubscriptionManager = eventSubscriptionManager;

this.eventSubscriptionManager
.AddSubscription<ReferenceValuesChangedEvent>(
x => x.Subscribe(this.ReferenceValuesChangedEventHandler));

this.AirPressureData = airPressureData;
this.CandleViewModelCollection =
new ObservableCollection<CandleViewModel>
{
this.firstCandle,
this.secondCandle,
this.thirdCandle
};
}

/// <summary>
/// Gets or sets the candle view model collection.
/// </summary>
public ObservableCollection<CandleViewModel> CandleViewModelCollection
{
get
{
return this.GetPropertyValue<ObservableCollection<CandleViewModel>>();
}

set
{
this.SetPropertyValue(value);
}
}

/// <summary>
/// Sets the AirPressureData.
/// </summary>
/// <value>
/// The AirPressureData.
/// </value>
private ProcessingCollection<double> AirPressureData
{
set
{
if (this.airPressureData != value)
{
if (this.airPressureData != null)
{
this.airPressureData.CollectionChanged -= this.AirPressureDataCollectionChanged;
}

this.airPressureData = value;

if (this.airPressureData != null)
{
this.airPressureData.CollectionChanged += this.AirPressureDataCollectionChanged;
}
}
}
}

/// <summary>
/// Disposes the resources used by this object
/// </summary>
/// <param name="isDisposing">
/// True if this method was called by the user,
/// false if it was called by the GC.
/// </param>
protected override void Dispose(bool isDisposing)
{
if (!this.isDisposed)
{
if (isDisposing)
{
this.AirPressureData = null;
}

this.isDisposed = true;
base.Dispose(isDisposing);
}
}

/// <summary>
/// Adjusts the candle animation states for all candles in the collection
/// </summary>
/// <param name="flow">
/// The flow.
/// </param>
private void AdjustCandleAnimationStates(double airPressure)
{
double index = 1d;
foreach (CandleViewModel candleViewModel in this.CandleViewModelCollection)
{
double? percentage = (airPressure / this.referenceAirPressure) * (index / this.CandleViewModelCollection.Count);
candleViewModel.CandleAnimationState = this.CalculateAnimationState(percentage);

index++;
}
}

/// <summary>
/// Event handler for the CollectionChangedEvent of AirPressureData.
/// </summary>
/// <param name="sender">
/// The sender.
/// </param>
/// <param name="e">
/// The <see cref="NotifyCollectionChangedEventArgs"/> instance containing the event data.
/// </param>
private void AirPressureDataCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
var newAirPressureData = sender as ProcessingCollection<ISampleDataSet>;

if (newAirPressureData != null && e.Action == NotifyCollectionChangedAction.Add)
{
foreach (double sampleData in e.NewItems.OfType<double>())
{
this.AdjustCandleAnimationStates(sampleData);
}
}
}

/// <summary>
/// Returns a <see cref="CandleAnimationState"/> depending on the flow/reference ratio
/// </summary>
/// <param name="percentage">
/// The percentage.
/// </param>
/// <returns>
/// The <see cref="CandleAnimationState"/>.
/// </returns>
private CandleAnimationState CalculateAnimationState(double? percentage)
{
if (percentage > 80)
{
return CandleAnimationState.Dead;
}

if (percentage > 40)
{
return CandleAnimationState.HighFlicker;
}

if (percentage > 10)
{
return CandleAnimationState.LowFlicker;
}

return CandleAnimationState.Idle;
}

/// <summary>
/// A event handler for the ReferenceValuesChangedEvent.
/// </summary>
/// <param name="referenceValues">
/// The reference values of the measurements
/// </param>
private void ReferenceValuesChangedEventHandler(
IDictionary<ParameterDefinitions, IReferenceResult> referenceValues)
{
if (referenceValues != null)
{
IReferenceResult referenceResult;
if (referenceValues.TryGetValue(AirPressureReference))
{
if (referenceResult.ReferenceValue.HasValue)
{
this.referenceAirPressure = referenceResult.ReferenceValue;
}
}
}
}
}


My candles are displayed via a ItemsControl:



<ItemsControl x:Name="CandleItemsControl" ItemsSource="{Binding CandleViewModelCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<candleControl:CandleView />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel HorizontalAlignment="Stretch" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>


Now to my questions:



  • How do I change storyboards(since that seems to be easier) for different candles, e.g. Candle 1 flickers a bit, while Candle 2 and 3 are still idle? All the Candles have the same ChildElements regarding their x:Name. I don´t want to use CodeBehind in my Views.

  • Do I need to generate each storyboard with its multiple animations and target properties on the fly, whenever the current CandleAnimationState changes?

  • Can I bind against a storyboard defined in my CandleViewModel and change its behaviour?

  • How do I set the needed TargetProperties in my ViewModel without really knowing the CandleView(since it is generated by the ItemsControl)?


My first idea was to hard code the candles into my View, so there are always 3 candles view-wise, but this doesn´t fit my intentions with MVVM.



asked 1 min ago







Re-use different storyboards in wpf for multiple but alike controls, MVVM

Aucun commentaire:

Enregistrer un commentaire